1. Introduction
This notebook contains a differential gene expression analysis and a preliminary thresholded over-representation analysis for a selected dataset from the GEO database. The dataset in particular is GSE157194, which is associated with a publication titled “Atopic dermatitis displays stable and dynamic skin transcriptome signatures” (Möbus et al., 2021).
The goal of the study was to understand the effect of various treatments for atopic dermatitis by conducting a gene expression study using mRNA-Seq data taken from biopsies of the affected and non-affected skin of human patients. The dataset contains data for 57 patients, with samples of affected (intrapersonal lesional) and non-affected (non-lesional) skin, along with follow-ups after three months of treatment (with either dupilumab or cyclosporine). An important note is that the dataset is “incomplete”/has several prominent issues:
- There is no distinction between patients and controls (or the control patients were omitted, which is likely)
- Not all patients have samples for both affected and non-affected skin
- Not all patients have follow-up data after treatment
- No patients are labelled with which drug (dupilumab/cyclosporine) was used as treatment
No information regarding these missing features were found in the associated paper.
After downloading the raw count matrix from GEO via GEOquery (Davis & Meltzer, 2007), the data was cleaned (confirming that there were no duplicate genes), filtered by gene (removing those without at least one read per million in each sample), and filtered by patient (removing those with incomplete data; see the points above). The genes (rows) were labelled by their Ensembl gene IDs, which were mapped to HUGO gene symbols using biomaRt (Durinck et al., 2009), and genes that were unable to be converted (such as novel genes) were removed. To normalize the data, trimmed mean of m-values/TMM was used (Robinson et al., 2010), since we followed the assumption that most genes are not differentially expressed. Finally, the data was checked for outliers (none were found), and the resulting data frame was written to a tab-delimited txt file (and gzipped for upload to GitHub).
We can load the dataset here and take a look at the first few rows and columns:
# The dataset should originally be compressed (GitHub 25 MB)
filepath <- file.path(getwd(), "normalizedCountData.txt")
if (!file.exists(filepath)) {
gzfilepath <- file.path(getwd(), "normalizedCountData.txt.gz")
if (!file.exists(gzfilepath)) {
stop("Dataset not found")
}
GEOquery::gunzip(gzfilepath)
}
normalizedCountData <- read.table(file = file.path(getwd(), "normalizedCountData.txt"),
sep = "\t",
header = TRUE,
stringsAsFactors = FALSE,
check.names = FALSE)
knitr::kable(normalizedCountData[1:4, 1:4], type = "html")
| A1BG-AS1 |
1.216988 |
1.0737920 |
1.901915 |
2.7945041 |
| A2M |
213.360038 |
263.8843886 |
386.515720 |
303.5713893 |
| A2M-AS1 |
1.880799 |
0.9203932 |
2.057173 |
0.6618562 |
| A2ML1 |
131.268699 |
143.3512345 |
156.034665 |
127.2234747 |
Number of patients: 24
Number of samples: 96
Number of genes: 17747
The column names are formatted “Patient_<patient number>_<AN|AL>_<m0|m3>“, where AN means intrapersonal non-lesional, AL means intrapersonal lesional, m0 means the sample was taken before treatment, and m3 means the sample was taken after three months of treatment. Note the absence of patient 2 between patients 1 and 3, who was removed due to missing data.
2. Differential gene expression
The corresponding journal entry for this assignment can be found here.
First, we should inspect the data as a heatmap to get a general idea of gene expression levels. But before we do that, we should row-normalize it, which scales rows and centres them around the mean, so that no particular gene completely dominates the plot.
# Parameters to select the range of samples to use. The matrix has 17,747 rows and 96 columns, and if too much data is
# chosen, the code will crash
SAMPLE_START <- 1
# Setting SAMPLE_END to 12 or more has been tested to crash
SAMPLE_END <- 8
heatmapMatrix <- normalizedCountData[, SAMPLE_START:SAMPLE_END]
# Row-normalization can be done with the base scale function on the transposed matrix
heatmapMatrix <- t(scale(t(heatmapMatrix)))
heatmapMatrix[is.na(heatmapMatrix)] <- 0
if (min(heatmapMatrix) == 0) {
# No negative values => colours should only range from white to red
heatmapColours <- circlize::colorRamp2(c(0, max(heatmapMatrix)),
c("white", "red"))
} else {
# Has negative values => use blue for those
heatmapColours <- circlize::colorRamp2(c(min(heatmapMatrix), 0, max(heatmapMatrix)),
c("blue", "white", "red"))
}
currHeatmap <- ComplexHeatmap::Heatmap(as.matrix(heatmapMatrix),
show_row_dend = TRUE,
show_column_dend = FALSE,
col = heatmapColours,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
cluster_columns = FALSE,
name = "Key",
column_title = "Normalized heatmap of samples")
# Displaying the heatmap may take a while to run, or just crash
currHeatmap

By default (and only if the last line in the code snippet directly above is not commented out), the heatmap will be displayed for eight samples (four patients, both before treatment). The parameters at the start of the code snippet can be changed to display different parts of the entire heatmap. Regardless, we can already see that there is significant variation in the data between different patients. This will need to be taken into account when doing further analysis.
Testing a single gene
The authors of the paper hypothesized that IL13 (interleukin 13, a gene related to type 2 cytokines and chemokines), among others, were responsible for the lesions found in patients with AD, so we can run a quick test to see if there is any change in expression for this particular gene among the entire set of patients.
# Get the columns corresponding to pre- and post-treatment affected samples
preTreatAffSamples <- grep(colnames(normalizedCountData), pattern = "_AL_m0")
postTreatAffSamples <- grep(colnames(normalizedCountData), pattern = "_AL_m3")
# Find the row index of IL13 in the data
il13Idx <- which(rownames(normalizedCountData) == "IL13")
# Format the expression levels (for both pre- and post-treatment) in a table for comparison
il13Expr <- t(normalizedCountData[il13Idx, preTreatAffSamples])
il13Expr <- as.data.frame(il13Expr)
il13Expr <- cbind(il13Expr, t(normalizedCountData[il13Idx, postTreatAffSamples]))
colnames(il13Expr) <- c("Pre-treatment", "Post-treatment")
# A visual inspection of some of the data may be interesting
knitr::kable(il13Expr[1:8, ], type = "html")
| Patient_1_AL_m0 |
0.2684480 |
0.2614197 |
| Patient_3_AL_m0 |
0.2206187 |
1.0196571 |
| Patient_4_AL_m0 |
0.8283549 |
0.0968785 |
| Patient_7_AL_m0 |
3.1572718 |
0.3413900 |
| Patient_8_AL_m0 |
0.1581532 |
0.2282131 |
| Patient_9_AL_m0 |
0.2365710 |
4.0256386 |
| Patient_11_AL_m0 |
0.2784309 |
0.9275250 |
| Patient_12_AL_m0 |
0.4850444 |
0.7312003 |
Looking at the expression change may not necessarily be conclusive, so we can also try a simple t-test.
t.test(x = t(il13Expr$`Pre-treatment`), y=t(il13Expr$`Post-treatment`))
Welch Two Sample t-test
data: t(il13Expr$`Pre-treatment`) and t(il13Expr$`Post-treatment`)
t = -0.85029, df = 42.098, p-value = 0.4
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-1.1635641 0.4736868
sample estimates:
mean of x mean of y
0.8118375 1.1567761
We see that the results of the test do not suggest a difference in expression before and after treatment for the entire set of patients, which provides evidence to suggest that patient variation is a major factor that needs to be accounted for.
Creating a model
To try and control for these variables, we can use various packages that provide methods specialized for differential expression analysis. In particular, edgeR is a package especially suited for bulk RNA-Seq analysis, containing several linear methods that we can use (Robinson et al., 2010). We start by setting up the counts header into individual categories, which should make it easier to use different parts of the data for different analyses or plots.
# Defines different groups that can be combined for different things (adapted from A1)
splitColNames <- function(colNames) {
return(unlist(strsplit(colNames, split = "_"))[c(2, 3, 4)])
}
groups <- data.frame(lapply(colnames(normalizedCountData), splitColNames))
colnames(groups) <- colnames(normalizedCountData)
rownames(groups) <- c("Patient", "Sample type", "Sample time")
groups <- data.frame(t(groups))
# Check the first few lines of the whole table to make sure the groups have been defined properly
knitr::kable(groups[1:4, ], type = "html")
| Patient_1_AN_m0 |
1 |
AN |
m0 |
| Patient_1_AL_m0 |
1 |
AL |
m0 |
| Patient_3_AN_m0 |
3 |
AN |
m0 |
| Patient_3_AL_m0 |
3 |
AL |
m0 |
Next, we can throw (parts of) the data into an MDS plot in order to get some insight as to how we should set up our linear models.
# Parameters to select which subset of data to plot
PATIENT_START <- 1
PATIENT_END <- 5
# Get the affected subset of the data
affectedCountData <- normalizedCountData[grep(colnames(normalizedCountData), pattern = "_AL_")
][, c(PATIENT_START:PATIENT_END, (PATIENT_START + 24):(PATIENT_END + 24))]
affectedGroups <- groups[groups$Sample.type == "AL",
][c(PATIENT_START:PATIENT_END, (PATIENT_START + 24):(PATIENT_END + 24)), ]
# Build the MDS plot, colouring by time (pre- and post-treatment)
dgelist <- edgeR::DGEList(counts = affectedCountData, group = affectedGroups$Sample.time)
edgeR::plotMDS.DGEList(dgelist, labels = rownames(affectedGroups),
col = c("darkgreen", "blue")[factor(affectedGroups$Sample.time)])
legend(x = "bottomleft", legend = c("Pre-treatment", "Post-treatment"), fill = c("darkgreen", "blue"))
title(main = "Distance between samples, coloured by treatment time")

Also try grouping by patient to see approximately how much variability there is.
# Colour by patient number
patientColours <- rainbow(PATIENT_END - PATIENT_START + 1)
edgeR::plotMDS.DGEList(dgelist, labels = rownames(affectedGroups),
col = patientColours)
title(main = "Distance between samples, coloured by patient number")

We see that there is no especially obvious clustering in treatment time or individual patient, which could be explained by the imperfect data; e.g., perhaps the patients shown here used different drugs or some were actually controls. Regardless, there is incentive to try fitting the data to a linear model to see if any improvements can be found.
edgeR provides a convenient quasi-likelihood method that has been recommended for bulk RNA-Seq experiments and is often used for more complicated models. Since the dataset that we are working with is quite large and complex, it may be a good candidate to use. We will first try a straightforward model that does not account for patient variability.
# Create a linear design matrix for edgeR quasi likelihood in treatment time
modelDesign <- model.matrix(~ groups$Sample.time)
dgelist <- edgeR::DGEList(counts = normalizedCountData, group = groups$Sample.time)
dgelist <- edgeR::estimateDisp(dgelist, modelDesign)
modelFit <- edgeR::glmQLFit(dgelist, modelDesign)
# Extract the results
qlfTime <- edgeR::glmQLFTest(modelFit, coef = "groups$Sample.timem3")
qlfHits <- edgeR::topTags(qlfTime, sort.by = "PValue", n = nrow(normalizedCountData))
knitr::kable(edgeR::topTags(qlfTime)[1:8, ], type = "html")
| CCL18 |
-2.5618699 |
6.650771 |
50.53415 |
0 |
3.50e-06 |
| SPARC |
1.3570281 |
10.440376 |
45.50124 |
0 |
8.60e-06 |
| BCL6 |
-0.5799921 |
6.810072 |
44.66884 |
0 |
8.60e-06 |
| CCL3-AS1 |
-1.8888323 |
1.666397 |
43.34914 |
0 |
1.03e-05 |
| CCL13 |
-1.5892900 |
5.469213 |
40.18952 |
0 |
2.55e-05 |
| FKBP5 |
-0.9104746 |
7.304668 |
39.31521 |
0 |
2.92e-05 |
| APLNR |
0.9643666 |
4.052355 |
38.77831 |
0 |
3.04e-05 |
| NPTX2 |
-1.2510772 |
2.645831 |
38.09414 |
0 |
3.42e-05 |
Number of genes that pass the threshold p-value (0.05): 2541
Number of genes that pass correction (FDR): 280
Since we are doing many tests, the probability that a positive result will occur simply by chance is increased. To control for this, we can use a method of multiple hypothesis testing, which will try to correct the positivity rate. edgeR’s quasi-likelihood method does this by using false discovery rate, a fairly common way to correct the data.
We see that a fairly large number of genes are differentially expressed, even after correction with FDR, so an even stronger threshold can be considered. This will allow for greater levels of confidence when analyzing the data, and will also put a greater emphasis on which genes can be considered to be more important because they are significantly differentially expressed.
Number of genes that pass a stricter p-value (0.01): 976
Number of genes that pass correction (FDR): 94
Even stricter thresholds could be considered (such as 0.001), but we do not want to completely throw away the weaker signal. We can next try to fit the data to a model that incorporates patient number into it, which may have better results in downstream analysis.
# Essentially the same as before, but with additional data
modelDesignPatient <- model.matrix(~ groups$Patient + groups$Sample.time)
dgelist <- edgeR::DGEList(counts = normalizedCountData, group = groups$Sample.time)
dgelist <- edgeR::estimateDisp(dgelist, modelDesignPatient)
modelFit <- edgeR::glmQLFit(dgelist, modelDesignPatient)
qlfTime <- edgeR::glmQLFTest(modelFit, coef = "groups$Sample.timem3")
qlfHitsPatient <- edgeR::topTags(qlfTime, sort.by = "PValue", n = nrow(normalizedCountData))
knitr::kable(edgeR::topTags(qlfTime[1:8, ]), type = "html")
| A2ML1 |
-0.4733038 |
7.791353 |
13.1409656 |
0.0005242 |
0.0041934 |
| A2M |
0.1952677 |
8.021805 |
4.0324669 |
0.0482401 |
0.1929606 |
| AAAS |
-0.0618142 |
4.961146 |
2.6766718 |
0.3108878 |
0.4984121 |
| A2M-AS1 |
0.2207703 |
1.697648 |
5.2621708 |
0.3623892 |
0.4984121 |
| A4GALT |
0.0833681 |
4.645910 |
0.7130782 |
0.4011140 |
0.4984121 |
| AACSP1 |
-0.2199525 |
1.581059 |
2.9539634 |
0.4035144 |
0.4984121 |
| AACS |
0.0655970 |
6.777246 |
0.6130520 |
0.4361106 |
0.4984121 |
| A1BG-AS1 |
0.0373999 |
1.985611 |
0.1546446 |
0.8453775 |
0.8453775 |
Number of genes that pass the threshold p-value (0.05): 3089
Number of genes that pass correction (FDR): 719
Number of genes that pass a stricter p-value (0.01): 1414
Number of genes that pass correction (FDR): 267
When applying a model that takes patient variability into account, we see that the number of genes that are significantly differentially expressed increases, which is not unexpected.
Genes of interest
A quick next step is to try and find genes of interest; e.g., those that are the most differentially expressed. One method of accomplishing this task is to use a volcano plot, which puts p-value against log-fold change. In this way, genes with the most meaningful or significant changes will be found near the top-left and top-right corners of the graph. Here, the R package EnhancedVolcano is used to quickly throw together such a plot (Blighe et al., 2021).
# Get the log-fold change and p-values from the patient-variability-included model
volcano <- data.frame(log2FC = qlfTime$table$logFC, PValue = qlfTime$table$PValue)
rownames(volcano) <- rownames(normalizedCountData)
EnhancedVolcano::EnhancedVolcano(volcano, lab = rownames(volcano), x = "log2FC", y = "PValue",
title = "Differentially expressed genes")

We see that the vast majority of genes (17,747 in total) are not significantly differentially expressed, being plotted toward the bottom-centre of the graph, but there are a few genes that present themselves as of interest. For example, the plot identifies CCL18, IL19, NPTX2, ADORA3, ALOX15, SPARC, ACSM6, and SPINK9 as genes with significant differences in levels of expression.
The next step is to return to looking at heatmaps; we can now plot the results of applying the quasi-likelihood model to the data, which may result in clearer clusters that can be visually identified.
# Using the stricter p-value threshold
topHits <- rownames(qlfHits$table)[qlfHits$table$PValue < 0.01]
# Warning: heatmapMatrix may only have been built with a subset of the data. topHeatmapMatrix here will reflect that
# subset. Change SAMPLE_START and SAMPLE_END in the code snippet at the start of this section to change the selected
# data
topHeatmapMatrix <- t(scale(t(heatmapMatrix[which(rownames(heatmapMatrix) %in% topHits), ])))
topHeatmapMatrix[is.na(topHeatmapMatrix)] <- 0
if (min(topHeatmapMatrix) == 0) {
heatmapColours <- circlize::colorRamp2(c(0, max(topHeatmapMatrix)),
c("white", "red"))
} else {
heatmapColours <- circlize::colorRamp2(c(min(topHeatmapMatrix), 0, max(topHeatmapMatrix)),
c("blue", "white", "red"))
}
groupColours <- c("blue", "red")
names(groupColours) <- unique(groups$Sample.type)
heatmapAnno <- ComplexHeatmap::HeatmapAnnotation(df = data.frame(Type = groups$Sample.type[SAMPLE_START:SAMPLE_END]),
col = list(type = groupColours))
currHeatmap <- ComplexHeatmap::Heatmap(as.matrix(topHeatmapMatrix),
show_row_dend = TRUE,
show_column_dend = TRUE,
col = heatmapColours,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
cluster_columns = FALSE,
name = "Key",
column_title = "Top hits heatmap",
top_annotation = heatmapAnno)
currHeatmap

This heatmap is not particularly useful in determining if differentially expressed genes cluster, because the columns are ordered such that individual patient data is paired together. For example, the first column here is the control sample for the first patient (the non-affected skin) pre-treatment, and the second column is the affected skin sample, also pre-treatment. It should be possible to get a better idea of clustering over time by reordering the columns.
# Build a new heatmap matrix to decouple from SAMPLE_START and SAMPLE_END
reHeatmapMatrix <- normalizedCountData
reHeatmapMatrix <- t(scale(t(reHeatmapMatrix)))
reHeatmapMatrix[is.na(reHeatmapMatrix)] <- 0
# Don't know how to do this in a one-liner
newIdxs <- integer(SAMPLE_END - SAMPLE_START + 1)
currIdx <- 1
# Add the *_AN_m0 first
for (i in seq(SAMPLE_START, SAMPLE_END, 2)) {
newIdxs[currIdx] <- i
currIdx <- currIdx + 1
}
# Then add the *_AN_m3
for (i in seq(SAMPLE_START, SAMPLE_END, 2)) {
newIdxs[currIdx] <- i + 48
currIdx <- currIdx + 1
}
topHeatmapMatrix <- t(scale(t(reHeatmapMatrix[which(rownames(reHeatmapMatrix) %in% topHits), ])))
topHeatmapMatrix[is.na(topHeatmapMatrix)] <- 0
topHeatmapMatrix <- topHeatmapMatrix[, newIdxs]
groupColours <- c("blue", "red")
names(groupColours) <- unique(groups$Sample.time)
heatmapAnno <- ComplexHeatmap::HeatmapAnnotation(df = data.frame(Time = groups$Sample.time[newIdxs]),
col = list(type = groupColours))
currHeatmap <- ComplexHeatmap::Heatmap(as.matrix(topHeatmapMatrix),
show_row_dend = TRUE,
show_column_dend = TRUE,
col = heatmapColours,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
cluster_columns = FALSE,
name = "Key",
column_title = "Top hits, control, over time",
top_annotation = heatmapAnno)
currHeatmap

The above heatmap has been rearranged such that all eight columns are control (non-affected/non-lesional) skin of affected patients, with the left four columns being samples taken just prior to treatment, and the right four samples being taken after three months of treatment. Since this subset of the dataset does not contain any samples of affected (lesional) skin, it is not surprising that levels of gene expression are similar across the two categories. The most obvious difference would be with respect to patient 3 (and patient 1, to a certain extent), where the follow-up sample has greater levels of regulation–but it is still of the same type.
# Doing the same as above except with *_AL_* instead of *_AN_*
newIdxs <- integer(SAMPLE_END - SAMPLE_START + 1)
currIdx <- 1
for (i in seq(SAMPLE_START + 1, SAMPLE_END + 1, 2)) {
newIdxs[currIdx] <- i
currIdx <- currIdx + 1
}
for (i in seq(SAMPLE_START + 1, SAMPLE_END + 1, 2)) {
newIdxs[currIdx] <- i + 48
currIdx <- currIdx + 1
}
topHeatmapMatrix <- t(scale(t(reHeatmapMatrix[which(rownames(reHeatmapMatrix) %in% topHits), ])))
topHeatmapMatrix[is.na(topHeatmapMatrix)] <- 0
topHeatmapMatrix <- topHeatmapMatrix[, newIdxs]
groupColours <- c("blue", "red")
names(groupColours) <- unique(groups$Sample.time)
heatmapAnno <- ComplexHeatmap::HeatmapAnnotation(df = data.frame(Time = groups$Sample.time[newIdxs]),
col = list(type = groupColours))
currHeatmap <- ComplexHeatmap::Heatmap(as.matrix(topHeatmapMatrix),
show_row_dend = TRUE,
show_column_dend = TRUE,
col = heatmapColours,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
cluster_columns = FALSE,
name = "Key",
column_title = "Top hits, over time",
top_annotation = heatmapAnno)
currHeatmap

When we look at the heatmap concerning affected skin, the differential expression is more obvious and more significant. There are many clusters of genes on the right side of the plot that correspond inversely to the same region on the left side of the plot. For example, the column of patient 7 has a large, strongly expressed group of genes near the middle of the column. However, when looking at the same area on the left side of the heatmap, the expression of the same genes is very weak in comparison. Thus, we can hypothesize that perhaps these differentially expressed genes may be responsible for AD and/or its symptoms.
3. Thresholded over-representation analysis
In this next part of the notebook, the sets of significantly up-regulated and down-regulated genes will be run through a thresholded gene set enrichment analysis. The first step is to get the lists of such genes.
# Construct the ranked gene list (non-thresholded)
qlfHitsT <- as.data.frame(qlfHitsPatient)
qlfHitsT["rank"] <- -log(qlfHitsT$PValue, base = 10) * sign(qlfHitsT$logFC)
qlfHitsT <- qlfHitsT[order(qlfHitsT$rank), ]
write.table(data.frame(genename = rownames(qlfHitsT), Fstat = qlfHitsT$rank),
file = file.path(getwd(), "rankedGenelist.txt"),
sep = "\t",
row.names = FALSE,
col.names = FALSE,
quote = FALSE)
# For the thresholded lists, use p = 0.05 instead of 0.01 to get more results
upregGenes <- rownames(qlfHitsT[which(qlfHitsT$PValue < 0.05 & qlfHitsT$logFC > 0), ])
write.table(upregGenes,
file = file.path(getwd(), "upregulatedGenes.txt"),
sep = "\t",
row.names = FALSE,
col.names = FALSE,
quote = FALSE)
downregGenes <- rownames(qlfHitsT[which(qlfHitsT$PValue < 0.05 & qlfHitsT$logFC < 0), ])
write.table(downregGenes,
file = file.path(getwd(), "downregulatedGenes.txt"),
sep = "\t",
row.names = FALSE,
col.names = FALSE,
quote = FALSE)
The technique that has been chosen to perform this analysis is Fisher’s exact test, done through g:Profiler (Kolberg et al., 2020). Fisher’s exact test is a common statistical method to check for overrepresentation, which is the goal of this analysis, and is able to calculate the deviation from p-values exactly. g:Profiler is a common, convenient (accessible online and through an R package) implementation of Fisher’s exact test that is updated fairly regularly, which can help increase the accuracy of the analysis. For our purpose, and in the interest of time, g:Profiler will be accessed through its website (instead of using the R package).
The annotation datasets that have been selected include GO biological processes (updated 2021-12-15) - without electronic annotations, Reactome (updated 2022-01-03), and WikiPathways (updated 2021-12-10). These three datasets are major annotation resources, and have been updated fairly recently, so it is reasonable to expect accuracte results. Other major resources such as KEGG have not been included because they may add additional extraneous information to the results, and with these three datasets, there should already be enough information for a thorough analysis.
We will start by looking at subsets of the genes of interest (i.e., those with significant differential expression), and then follow up by analyzing the entire set to compare how looking at different parts of the data may affect the results. For each run, FDR will be used for multiple hypothesis correction, with a threshold p-value of 0.05. This was chosen instead of the more stringent 0.01 to allow for more information and a broader look at the associated pathways. In addition, the resulting pathways were filtered to only include those with gene set sizes of 200 or less. This is because of the inheritance-like property of the annotation resources, which may clutter the results by including pathways that are too generic.
Upregulated genes
The first subset is all upregulated genes with significant differential expression. The list of upregulated genes (from the code snippet above) was copied to g:Profiler, the mentioned options were set (with all results checked), and the analysis was run. The result was the following figure:
g:Profiler has omitted titles for the plot and table, but appropriate ones would be “Over-representation of upregulated genes with p-value < 0.05” and “Highest scoring pathways for gene sets with size less than 200,” respectively.
Using the thresholds mentioned previously, GO:BP found 5,027 pathways, REAC found 1,007, and WP found 445. The plot at the top of the figure is a visual representation of adjusted p-values vs. resource, where each dot represents a specific pathway (each annotation resource has its own colour). The table at the bottom of the figure is a list of the top (approximately 15) pathways, according to adjusted p-value, for each resource. The individual pathways are given numbers, and these correspond to the labelling of the dots in the plot above. We see that despite being the pathways with the greatest adjusted p-value, these labelled dots are not the highest on the plot because of the thresholds that were used; the unlabelled dots would represent more generic pathways.
The specific pathways that were found here–for example, collagen fibril organization and several fatty acid metabolic processes–are not obviously indicative of AD, a skin disease of autoimmune origin.
Downregulated genes
The second subset is all downregulated genes with significant differential expression. The list of downregulated genes was copied to g:Profiler, options were set (this time without all results, since the list of genes was too large), and the analysis was run. The result was the following figure:
g:Profiler has omitted titles for the plot and table, but appropriate ones would be “Over-representation of downregulated genes with p-value < 0.05” and “Highest scoring pathways for gene sets with size less than 200,” respectively.
Using the thresholds mentioned previously, GO:BP found 230 pathways, REAC found 72, and WP found 52. The specific pathways that were found here–for example, various DNA replication pathways and cell cycle signalling–is, like the upregulated genes, not directly related to AD. However, these examples are not as broad, and it is possible that there is an indirect link. For example, psoriasis, another skin condition similar to atopic dermatitis, is directly related to the disregulation of the cell cycle of skin cells, so it would not be a surprise to see a similar result here.
All differentially expressed genes
It is important to consider the entire set of differentially expressed genes as a whole, because it is possible that the particular pathways that we are expecting have both upregulated and downregulated genes. The result of running g:Profiler on the list of combined genes is as follows:
g:Profiler has omitted titles for the plot and table, but appropriate ones would be “Over-representation of significant differentially expressed genes with p-value < 0.05” and “Highest scoring pathways for gene sets with size less than 200,” respectively.
Using the thresholds mentioned previously, GO:BP found 130 pathways, REAC found 37, and WP found 15. When we put both the upregulated genes and downregulated genes together through over-representation analysis, it appears that we get a better overall image of the pathways that AD affects. For example, the top GO:BP hit is now directly related to skin development, an obvious link to AD. In addition, we see other pathways related to immune function; for example, the Toll-like receptor 4 (TLR4) cascade, which is responsible for the production of cytokines.
Since many of these pathways were not present when only the up- or downregulated genes were considered, it is possible to conclude that AD is involved in more complex pathways that cannot be restricted to just one type of gene. The entire set of differentially expressed genes needs to be considered, and only then will the relevant pathways appear.
4. Interpretation
With regard to the differential expression analysis, several of the most significant genes that were found were also found by the authors of the original paper. For example, one with a particularly high level of expression was IL19, with the paper stating that they “detected a notable expression of IL19 … in AL,” exactly what was found here. However, this analysis did find some highly expressed genes that were not of note in the paper; for example, NPTX2, related to the C-reactive protein, was not noted in the paper. Additionally, other genes like IL31 and IL23 that were noted in the paper were not among the most expressed genes here. Thus, we can say that for this part of the notebook, the results are at least partially supported by the original paper.
When looking at pathways, the result of over-representation analysis, we see some similarities between those that were found here and those that were found in the original paper. The paper states that the “core transcriptome signature” of AD patients is characterized by epidermal differentiation and itch pathways, and gives sebaceous glands as an example. In the ORA of all differentially expressed genes, the highest scored pathway is related to skin development, of which “sebaceous gland development” is a child.
Additional pathways that were found in both include general Toll-like receptor signalling, interleukin signalling, and several other immune-related pathways. However, the paper does mention specific examples like Th17 cell differentiation and the IL-17 signalling pathway, which were not exactly found in the ORA. Thus, we should be able to conclude that for this part of the notebook, the results overall are supported by the original paper, but in order to confirm the presence of more specific pathways, more detailed examination must be done.
When considering the differentially expressed genes and pathways, another way of looking for evidence to support that the analyses in this notebook (other than comparing with the original paper) is to look for additional papers that have reached the same conclusion. One study (Oka et al., 2015) showed that IL-19, one of the most significantly expressed interleukin genes found here, was notably increased in patients with AD. Statistical analysis (primarily done via the Mann-Whitney U test) was performed, and in summary, concluded that there was statistically significant evidence to suggest that AD patients had elevated IL-19 protein levels, a reflection of significant expression.
A second paper (Panzer et al., 2014) found that AD patients also have significant levels of TLR4 expression as part of Toll-like receptor signalling pathways. Samples from lesional skin were taken, incubated with TLR4 antibodies, and then RNA was extracted, which was found to show constitutive expression of TLR4. There have even been previous studies on using dupilumab and cyclosporine to treat AD; a thid paper (Mansouri & Guttman-Yassky, 2015) specifically studied the effects of these drugs on human immune pathways, and found that they are able to block some interleukin signalling pathways, which in turn resulted in clinical improvements. Thus, this shows that there is further support for the pathways discovered by the ORA conducted here.
LS0tCnRpdGxlOiAiQkNCNDIwIEEyIC0gRGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmQgcHJlbGltaW5hcnkgT1JBIgphdXRob3I6ICJZaWppYSBDaGVuIgpkYXRlOiAiMjAyMi0wMy0xNiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKYmlibGlvZ3JhcGh5OiBBMl9jaXRhdGlvbnMuYmliCmNzbDogYXBhLmNzbApub2NpdGU6ICJAKiIKLS0tCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGluY2x1ZGUgPSBGQUxTRX0KIyBTZXZlcmFsIFIgcGFja2FnZXMgYXJlIHJlcXVpcmVkIHRvIHJ1biB0aGlzIG5vdGVib29rLiBUaGV5IGNhbiBiZSBpbnN0YWxsZWQgYnkgcnVubmluZyB0aGlzIHNuaXBwZXQKIyBUaGlzIHNuaXBwZXQgd2lsbCBub3QgYmUgaW5jbHVkZWQgaW4gdGhlIGZpbmFsIHJlcG9ydCBiZWNhdXNlIHRoZSBpbnN0YWxsYXRpb24gb2YgRW5oYW5jZWRWb2xjYW5vIHdpbGwgYWx3YXlzCiMgZ2VuZXJhdGUgb3V0cHV0IHRoYXQgY2Fubm90IGJlIHN1cHByZXNzZWQgYnkgbWVzc2FnZSA9IEZBTFNFIG9yIHdhcm5pbmcgPSBGQUxTRQoKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkgewogIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKfQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkdFT3F1ZXJ5IiwgcXVpZXRseSA9IFRSVUUpKSB7CiAgQmlvQ01hbmFnZXI6Omluc3RhbGwoIkdFT3F1ZXJ5IikKfQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoImtuaXRyIiwgcXVpZXRseSA9IFRSVUUpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQp9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiZWRnZVIiLCBxdWlldGx5ID0gVFJVRSkpIHsKICBCaW9DTWFuYWdlcjo6aW5zdGFsbCgiZWRnZVIiKQp9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiQ29tcGxleEhlYXRtYXAiLCBxdWlldGx5ID0gVFJVRSkpIHsKICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiQ29tcGxleEhlYXRtYXAiKQp9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiY2lyY2xpemUiLCBxdWlldGx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJjaXJjbGl6ZSIpCn0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJFbmhhbmNlZFZvbGNhbm8iLCBxdWlldGx5ID0gVFJVRSkpIHsKICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiRW5oYW5jZWRWb2xjYW5vIiwgdXBkYXRlID0gRkFMU0UsIGFzayA9IEZBTFNFKQp9CmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpgYGAKCiMjIDEuIEludHJvZHVjdGlvbgoKVGhpcyBub3RlYm9vayBjb250YWlucyBhIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMgYW5kIGEgcHJlbGltaW5hcnkgdGhyZXNob2xkZWQgb3Zlci1yZXByZXNlbnRhdGlvbgphbmFseXNpcyBmb3IgYSBzZWxlY3RlZCBkYXRhc2V0IGZyb20gdGhlIEdFTyBkYXRhYmFzZS4gVGhlIGRhdGFzZXQgaW4gcGFydGljdWxhciBpcwpbR1NFMTU3MTk0XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0UxNTcxOTQpLCB3aGljaCBpcyBhc3NvY2lhdGVkIHdpdGggYSBwdWJsaWNhdGlvbgp0aXRsZWQgIkF0b3BpYyBkZXJtYXRpdGlzIGRpc3BsYXlzIHN0YWJsZSBhbmQgZHluYW1pYyBza2luIHRyYW5zY3JpcHRvbWUgc2lnbmF0dXJlcyIgW0Btb2J1c10uCgpUaGUgZ29hbCBvZiB0aGUgc3R1ZHkgd2FzIHRvIHVuZGVyc3RhbmQgdGhlIGVmZmVjdCBvZiB2YXJpb3VzIHRyZWF0bWVudHMgZm9yIGF0b3BpYyBkZXJtYXRpdGlzIGJ5IGNvbmR1Y3RpbmcgYSBnZW5lCmV4cHJlc3Npb24gc3R1ZHkgdXNpbmcgbVJOQS1TZXEgZGF0YSB0YWtlbiBmcm9tIGJpb3BzaWVzIG9mIHRoZSBhZmZlY3RlZCBhbmQgbm9uLWFmZmVjdGVkIHNraW4gb2YgaHVtYW4gcGF0aWVudHMuIFRoZQpkYXRhc2V0IGNvbnRhaW5zIGRhdGEgZm9yIDU3IHBhdGllbnRzLCB3aXRoIHNhbXBsZXMgb2YgYWZmZWN0ZWQgKGludHJhcGVyc29uYWwgbGVzaW9uYWwpIGFuZCBub24tYWZmZWN0ZWQKKG5vbi1sZXNpb25hbCkgc2tpbiwgYWxvbmcgd2l0aCBmb2xsb3ctdXBzIGFmdGVyIHRocmVlIG1vbnRocyBvZiB0cmVhdG1lbnQgKHdpdGggZWl0aGVyIGR1cGlsdW1hYiBvciBjeWNsb3Nwb3JpbmUpLiBBbgppbXBvcnRhbnQgbm90ZSBpcyB0aGF0IHRoZSBkYXRhc2V0IGlzICJpbmNvbXBsZXRlIi9oYXMgc2V2ZXJhbCBwcm9taW5lbnQgaXNzdWVzOgoKLSBUaGVyZSBpcyBubyBkaXN0aW5jdGlvbiBiZXR3ZWVuIHBhdGllbnRzIGFuZCBjb250cm9scyAob3IgdGhlIGNvbnRyb2wgcGF0aWVudHMgd2VyZSBvbWl0dGVkLCB3aGljaCBpcyBsaWtlbHkpCi0gTm90IGFsbCBwYXRpZW50cyBoYXZlIHNhbXBsZXMgZm9yIGJvdGggYWZmZWN0ZWQgYW5kIG5vbi1hZmZlY3RlZCBza2luCi0gTm90IGFsbCBwYXRpZW50cyBoYXZlIGZvbGxvdy11cCBkYXRhIGFmdGVyIHRyZWF0bWVudAotIE5vIHBhdGllbnRzIGFyZSBsYWJlbGxlZCB3aXRoIHdoaWNoIGRydWcgKGR1cGlsdW1hYi9jeWNsb3Nwb3JpbmUpIHdhcyB1c2VkIGFzIHRyZWF0bWVudAoKTm8gaW5mb3JtYXRpb24gcmVnYXJkaW5nIHRoZXNlIG1pc3NpbmcgZmVhdHVyZXMgd2VyZSBmb3VuZCBpbiB0aGUgYXNzb2NpYXRlZCBwYXBlci4KCkFmdGVyIGRvd25sb2FkaW5nIHRoZSByYXcgY291bnQgbWF0cml4IGZyb20gR0VPIHZpYSBHRU9xdWVyeSBbQGdlb3F1ZXJ5XSwgdGhlIGRhdGEgd2FzIGNsZWFuZWQgKGNvbmZpcm1pbmcgdGhhdCB0aGVyZQp3ZXJlIG5vIGR1cGxpY2F0ZSBnZW5lcyksIGZpbHRlcmVkIGJ5IGdlbmUgKHJlbW92aW5nIHRob3NlIHdpdGhvdXQgYXQgbGVhc3Qgb25lIHJlYWQgcGVyIG1pbGxpb24gaW4gZWFjaCBzYW1wbGUpLCBhbmQKZmlsdGVyZWQgYnkgcGF0aWVudCAocmVtb3ZpbmcgdGhvc2Ugd2l0aCBpbmNvbXBsZXRlIGRhdGE7IHNlZSB0aGUgcG9pbnRzIGFib3ZlKS4gVGhlIGdlbmVzIChyb3dzKSB3ZXJlIGxhYmVsbGVkIGJ5CnRoZWlyIEVuc2VtYmwgZ2VuZSBJRHMsIHdoaWNoIHdlcmUgbWFwcGVkIHRvIEhVR08gZ2VuZSBzeW1ib2xzIHVzaW5nIGJpb21hUnQgW0BiaW9tYXJ0XSwgYW5kIGdlbmVzIHRoYXQgd2VyZSB1bmFibGUgdG8KYmUgY29udmVydGVkIChzdWNoIGFzIG5vdmVsIGdlbmVzKSB3ZXJlIHJlbW92ZWQuIFRvIG5vcm1hbGl6ZSB0aGUgZGF0YSwgdHJpbW1lZCBtZWFuIG9mIG0tdmFsdWVzL1RNTSB3YXMgdXNlZCBbQGVkZ2VyXSwKc2luY2Ugd2UgZm9sbG93ZWQgdGhlIGFzc3VtcHRpb24gdGhhdCBtb3N0IGdlbmVzIGFyZSBub3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiBGaW5hbGx5LCB0aGUgZGF0YSB3YXMgY2hlY2tlZCBmb3IKb3V0bGllcnMgKG5vbmUgd2VyZSBmb3VuZCksIGFuZCB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgd2FzIHdyaXR0ZW4gdG8gYSB0YWItZGVsaW1pdGVkIHR4dCBmaWxlIChhbmQgZ3ppcHBlZCBmb3IKdXBsb2FkIHRvIEdpdEh1YikuCgpXZSBjYW4gbG9hZCB0aGUgZGF0YXNldCBoZXJlIGFuZCB0YWtlIGEgbG9vayBhdCB0aGUgZmlyc3QgZmV3IHJvd3MgYW5kIGNvbHVtbnM6CmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBUaGUgZGF0YXNldCBzaG91bGQgb3JpZ2luYWxseSBiZSBjb21wcmVzc2VkIChHaXRIdWIgMjUgTUIpCmZpbGVwYXRoIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAibm9ybWFsaXplZENvdW50RGF0YS50eHQiKQppZiAoIWZpbGUuZXhpc3RzKGZpbGVwYXRoKSkgewogIGd6ZmlsZXBhdGggPC0gZmlsZS5wYXRoKGdldHdkKCksICJub3JtYWxpemVkQ291bnREYXRhLnR4dC5neiIpCiAgaWYgKCFmaWxlLmV4aXN0cyhnemZpbGVwYXRoKSkgewogICAgc3RvcCgiRGF0YXNldCBub3QgZm91bmQiKQogIH0KICBHRU9xdWVyeTo6Z3VuemlwKGd6ZmlsZXBhdGgpCn0KCm5vcm1hbGl6ZWRDb3VudERhdGEgPC0gcmVhZC50YWJsZShmaWxlID0gZmlsZS5wYXRoKGdldHdkKCksICJub3JtYWxpemVkQ291bnREYXRhLnR4dCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEZBTFNFKQprbml0cjo6a2FibGUobm9ybWFsaXplZENvdW50RGF0YVsxOjQsIDE6NF0sIHR5cGUgPSAiaHRtbCIpCmBgYAoKTnVtYmVyIG9mIHBhdGllbnRzOiBgciBuY29sKG5vcm1hbGl6ZWRDb3VudERhdGEpIC8gNGAgIApOdW1iZXIgb2Ygc2FtcGxlczogYHIgbmNvbChub3JtYWxpemVkQ291bnREYXRhKWAgIApOdW1iZXIgb2YgZ2VuZXM6IGByIG5yb3cobm9ybWFsaXplZENvdW50RGF0YSlgCgpUaGUgY29sdW1uIG5hbWVzIGFyZSBmb3JtYXR0ZWQgIlBhdGllbnRfXDxwYXRpZW50IG51bWJlclw+XF9cPEFOfEFMXD5fXDxtMHxtM1w+Iiwgd2hlcmUgQU4gbWVhbnMgaW50cmFwZXJzb25hbApub24tbGVzaW9uYWwsIEFMIG1lYW5zIGludHJhcGVyc29uYWwgbGVzaW9uYWwsIG0wIG1lYW5zIHRoZSBzYW1wbGUgd2FzIHRha2VuIGJlZm9yZSB0cmVhdG1lbnQsIGFuZCBtMyBtZWFucyB0aGUgc2FtcGxlCndhcyB0YWtlbiBhZnRlciB0aHJlZSBtb250aHMgb2YgdHJlYXRtZW50LiBOb3RlIHRoZSBhYnNlbmNlIG9mIHBhdGllbnQgMiBiZXR3ZWVuIHBhdGllbnRzIDEgYW5kIDMsIHdobyB3YXMgcmVtb3ZlZCBkdWUKdG8gbWlzc2luZyBkYXRhLgoKIyMgMi4gRGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbgoKVGhlIGNvcnJlc3BvbmRpbmcgam91cm5hbCBlbnRyeSBmb3IgdGhpcyBhc3NpZ25tZW50IGNhbiBiZSBmb3VuZApbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2JjYjQyMC0yMDIyL1lpamlhX0NoZW4vd2lraS84Li1Bc3NpZ25tZW50LTItJUUyJTgwJTkwLUFkZGl0aW9uYWwtd29yay1hbmQtZGV0YWlscykuCgpGaXJzdCwgd2Ugc2hvdWxkIGluc3BlY3QgdGhlIGRhdGEgYXMgYSBoZWF0bWFwIHRvIGdldCBhIGdlbmVyYWwgaWRlYSBvZiBnZW5lIGV4cHJlc3Npb24gbGV2ZWxzLiBCdXQgYmVmb3JlIHdlIGRvIHRoYXQsCndlIHNob3VsZCByb3ctbm9ybWFsaXplIGl0LCB3aGljaCBzY2FsZXMgcm93cyBhbmQgY2VudHJlcyB0aGVtIGFyb3VuZCB0aGUgbWVhbiwgc28gdGhhdCBubyBwYXJ0aWN1bGFyIGdlbmUgY29tcGxldGVseQpkb21pbmF0ZXMgdGhlIHBsb3QuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgUGFyYW1ldGVycyB0byBzZWxlY3QgdGhlIHJhbmdlIG9mIHNhbXBsZXMgdG8gdXNlLiBUaGUgbWF0cml4IGhhcyAxNyw3NDcgcm93cyBhbmQgOTYgY29sdW1ucywgYW5kIGlmIHRvbyBtdWNoIGRhdGEgaXMKIyBjaG9zZW4sIHRoZSBjb2RlIHdpbGwgY3Jhc2gKU0FNUExFX1NUQVJUIDwtIDEKIyBTZXR0aW5nIFNBTVBMRV9FTkQgdG8gMTIgb3IgbW9yZSBoYXMgYmVlbiB0ZXN0ZWQgdG8gY3Jhc2gKU0FNUExFX0VORCA8LSA4CmhlYXRtYXBNYXRyaXggPC0gbm9ybWFsaXplZENvdW50RGF0YVssIFNBTVBMRV9TVEFSVDpTQU1QTEVfRU5EXQoKIyBSb3ctbm9ybWFsaXphdGlvbiBjYW4gYmUgZG9uZSB3aXRoIHRoZSBiYXNlIHNjYWxlIGZ1bmN0aW9uIG9uIHRoZSB0cmFuc3Bvc2VkIG1hdHJpeApoZWF0bWFwTWF0cml4IDwtIHQoc2NhbGUodChoZWF0bWFwTWF0cml4KSkpCmhlYXRtYXBNYXRyaXhbaXMubmEoaGVhdG1hcE1hdHJpeCldIDwtIDAKCmlmIChtaW4oaGVhdG1hcE1hdHJpeCkgPT0gMCkgewogICMgTm8gbmVnYXRpdmUgdmFsdWVzID0+IGNvbG91cnMgc2hvdWxkIG9ubHkgcmFuZ2UgZnJvbSB3aGl0ZSB0byByZWQKICBoZWF0bWFwQ29sb3VycyA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKDAsIG1heChoZWF0bWFwTWF0cml4KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygid2hpdGUiLCAicmVkIikpCn0gZWxzZSB7CiAgIyBIYXMgbmVnYXRpdmUgdmFsdWVzID0+IHVzZSBibHVlIGZvciB0aG9zZQogIGhlYXRtYXBDb2xvdXJzIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMobWluKGhlYXRtYXBNYXRyaXgpLCAwLCBtYXgoaGVhdG1hcE1hdHJpeCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KCmN1cnJIZWF0bWFwIDwtIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwTWF0cml4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gaGVhdG1hcENvbG91cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIktleSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90aXRsZSA9ICJOb3JtYWxpemVkIGhlYXRtYXAgb2Ygc2FtcGxlcyIpCgojIERpc3BsYXlpbmcgdGhlIGhlYXRtYXAgbWF5IHRha2UgYSB3aGlsZSB0byBydW4sIG9yIGp1c3QgY3Jhc2gKY3VyckhlYXRtYXAKYGBgCgpCeSBkZWZhdWx0IChhbmQgb25seSBpZiB0aGUgbGFzdCBsaW5lIGluIHRoZSBjb2RlIHNuaXBwZXQgZGlyZWN0bHkgYWJvdmUgaXMgbm90IGNvbW1lbnRlZCBvdXQpLCB0aGUgaGVhdG1hcCB3aWxsIGJlCmRpc3BsYXllZCBmb3IgZWlnaHQgc2FtcGxlcyAoZm91ciBwYXRpZW50cywgYm90aCBiZWZvcmUgdHJlYXRtZW50KS4gVGhlIHBhcmFtZXRlcnMgYXQgdGhlIHN0YXJ0IG9mIHRoZSBjb2RlIHNuaXBwZXQgY2FuCmJlIGNoYW5nZWQgdG8gZGlzcGxheSBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIGVudGlyZSBoZWF0bWFwLiBSZWdhcmRsZXNzLCB3ZSBjYW4gYWxyZWFkeSBzZWUgdGhhdCB0aGVyZSBpcyBzaWduaWZpY2FudAp2YXJpYXRpb24gaW4gdGhlIGRhdGEgYmV0d2VlbiBkaWZmZXJlbnQgcGF0aWVudHMuIFRoaXMgd2lsbCBuZWVkIHRvIGJlIHRha2VuIGludG8gYWNjb3VudCB3aGVuIGRvaW5nIGZ1cnRoZXIgYW5hbHlzaXMuCgojIyMgVGVzdGluZyBhIHNpbmdsZSBnZW5lCgpUaGUgYXV0aG9ycyBvZiB0aGUgcGFwZXIgaHlwb3RoZXNpemVkIHRoYXQgSUwxMyAoaW50ZXJsZXVraW4gMTMsIGEgZ2VuZSByZWxhdGVkIHRvIHR5cGUgMiBjeXRva2luZXMgYW5kIGNoZW1va2luZXMpLAphbW9uZyBvdGhlcnMsIHdlcmUgcmVzcG9uc2libGUgZm9yIHRoZSBsZXNpb25zIGZvdW5kIGluIHBhdGllbnRzIHdpdGggQUQsIHNvIHdlIGNhbiBydW4gYSBxdWljayB0ZXN0IHRvIHNlZSBpZiB0aGVyZSBpcwphbnkgY2hhbmdlIGluIGV4cHJlc3Npb24gZm9yIHRoaXMgcGFydGljdWxhciBnZW5lIGFtb25nIHRoZSBlbnRpcmUgc2V0IG9mIHBhdGllbnRzLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIEdldCB0aGUgY29sdW1ucyBjb3JyZXNwb25kaW5nIHRvIHByZS0gYW5kIHBvc3QtdHJlYXRtZW50IGFmZmVjdGVkIHNhbXBsZXMKcHJlVHJlYXRBZmZTYW1wbGVzIDwtIGdyZXAoY29sbmFtZXMobm9ybWFsaXplZENvdW50RGF0YSksIHBhdHRlcm4gPSAiX0FMX20wIikKcG9zdFRyZWF0QWZmU2FtcGxlcyA8LSBncmVwKGNvbG5hbWVzKG5vcm1hbGl6ZWRDb3VudERhdGEpLCBwYXR0ZXJuID0gIl9BTF9tMyIpCgojIEZpbmQgdGhlIHJvdyBpbmRleCBvZiBJTDEzIGluIHRoZSBkYXRhCmlsMTNJZHggPC0gd2hpY2gocm93bmFtZXMobm9ybWFsaXplZENvdW50RGF0YSkgPT0gIklMMTMiKQoKIyBGb3JtYXQgdGhlIGV4cHJlc3Npb24gbGV2ZWxzIChmb3IgYm90aCBwcmUtIGFuZCBwb3N0LXRyZWF0bWVudCkgaW4gYSB0YWJsZSBmb3IgY29tcGFyaXNvbgppbDEzRXhwciA8LSB0KG5vcm1hbGl6ZWRDb3VudERhdGFbaWwxM0lkeCwgcHJlVHJlYXRBZmZTYW1wbGVzXSkKaWwxM0V4cHIgPC0gYXMuZGF0YS5mcmFtZShpbDEzRXhwcikKaWwxM0V4cHIgPC0gY2JpbmQoaWwxM0V4cHIsIHQobm9ybWFsaXplZENvdW50RGF0YVtpbDEzSWR4LCBwb3N0VHJlYXRBZmZTYW1wbGVzXSkpCmNvbG5hbWVzKGlsMTNFeHByKSA8LSBjKCJQcmUtdHJlYXRtZW50IiwgIlBvc3QtdHJlYXRtZW50IikKCiMgQSB2aXN1YWwgaW5zcGVjdGlvbiBvZiBzb21lIG9mIHRoZSBkYXRhIG1heSBiZSBpbnRlcmVzdGluZwprbml0cjo6a2FibGUoaWwxM0V4cHJbMTo4LCBdLCB0eXBlID0gImh0bWwiKQpgYGAKCkxvb2tpbmcgYXQgdGhlIGV4cHJlc3Npb24gY2hhbmdlIG1heSBub3QgbmVjZXNzYXJpbHkgYmUgY29uY2x1c2l2ZSwgc28gd2UgY2FuIGFsc28gdHJ5IGEgc2ltcGxlIHQtdGVzdC4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KdC50ZXN0KHggPSB0KGlsMTNFeHByJGBQcmUtdHJlYXRtZW50YCksIHk9dChpbDEzRXhwciRgUG9zdC10cmVhdG1lbnRgKSkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgcmVzdWx0cyBvZiB0aGUgdGVzdCBkbyBub3Qgc3VnZ2VzdCBhIGRpZmZlcmVuY2UgaW4gZXhwcmVzc2lvbiBiZWZvcmUgYW5kIGFmdGVyIHRyZWF0bWVudCBmb3IgdGhlIGVudGlyZQpzZXQgb2YgcGF0aWVudHMsIHdoaWNoIHByb3ZpZGVzIGV2aWRlbmNlIHRvIHN1Z2dlc3QgdGhhdCBwYXRpZW50IHZhcmlhdGlvbiBpcyBhIG1ham9yIGZhY3RvciB0aGF0IG5lZWRzIHRvIGJlIGFjY291bnRlZApmb3IuCgojIyMgQ3JlYXRpbmcgYSBtb2RlbAoKVG8gdHJ5IGFuZCBjb250cm9sIGZvciB0aGVzZSB2YXJpYWJsZXMsIHdlIGNhbiB1c2UgdmFyaW91cyBwYWNrYWdlcyB0aGF0IHByb3ZpZGUgbWV0aG9kcyBzcGVjaWFsaXplZCBmb3IgZGlmZmVyZW50aWFsCmV4cHJlc3Npb24gYW5hbHlzaXMuIEluIHBhcnRpY3VsYXIsIGVkZ2VSIGlzIGEgcGFja2FnZSBlc3BlY2lhbGx5IHN1aXRlZCBmb3IgYnVsayBSTkEtU2VxIGFuYWx5c2lzLCBjb250YWluaW5nIHNldmVyYWwKbGluZWFyIG1ldGhvZHMgdGhhdCB3ZSBjYW4gdXNlIFtAZWRnZXJdLiBXZSBzdGFydCBieSBzZXR0aW5nIHVwIHRoZSBjb3VudHMgaGVhZGVyIGludG8gaW5kaXZpZHVhbCBjYXRlZ29yaWVzLCB3aGljaApzaG91bGQgbWFrZSBpdCBlYXNpZXIgdG8gdXNlIGRpZmZlcmVudCBwYXJ0cyBvZiB0aGUgZGF0YSBmb3IgZGlmZmVyZW50IGFuYWx5c2VzIG9yIHBsb3RzLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIERlZmluZXMgZGlmZmVyZW50IGdyb3VwcyB0aGF0IGNhbiBiZSBjb21iaW5lZCBmb3IgZGlmZmVyZW50IHRoaW5ncyAoYWRhcHRlZCBmcm9tIEExKQpzcGxpdENvbE5hbWVzIDwtIGZ1bmN0aW9uKGNvbE5hbWVzKSB7CiAgcmV0dXJuKHVubGlzdChzdHJzcGxpdChjb2xOYW1lcywgc3BsaXQgPSAiXyIpKVtjKDIsIDMsIDQpXSkKfQpncm91cHMgPC0gZGF0YS5mcmFtZShsYXBwbHkoY29sbmFtZXMobm9ybWFsaXplZENvdW50RGF0YSksIHNwbGl0Q29sTmFtZXMpKQpjb2xuYW1lcyhncm91cHMpIDwtIGNvbG5hbWVzKG5vcm1hbGl6ZWRDb3VudERhdGEpCnJvd25hbWVzKGdyb3VwcykgPC0gYygiUGF0aWVudCIsICJTYW1wbGUgdHlwZSIsICJTYW1wbGUgdGltZSIpCmdyb3VwcyA8LSBkYXRhLmZyYW1lKHQoZ3JvdXBzKSkKCiMgQ2hlY2sgdGhlIGZpcnN0IGZldyBsaW5lcyBvZiB0aGUgd2hvbGUgdGFibGUgdG8gbWFrZSBzdXJlIHRoZSBncm91cHMgaGF2ZSBiZWVuIGRlZmluZWQgcHJvcGVybHkKa25pdHI6OmthYmxlKGdyb3Vwc1sxOjQsIF0sIHR5cGUgPSAiaHRtbCIpCmBgYAoKTmV4dCwgd2UgY2FuIHRocm93IChwYXJ0cyBvZikgdGhlIGRhdGEgaW50byBhbiBNRFMgcGxvdCBpbiBvcmRlciB0byBnZXQgc29tZSBpbnNpZ2h0IGFzIHRvIGhvdyB3ZSBzaG91bGQgc2V0IHVwIG91cgpsaW5lYXIgbW9kZWxzLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIFBhcmFtZXRlcnMgdG8gc2VsZWN0IHdoaWNoIHN1YnNldCBvZiBkYXRhIHRvIHBsb3QKUEFUSUVOVF9TVEFSVCA8LSAxClBBVElFTlRfRU5EIDwtIDUKIyBHZXQgdGhlIGFmZmVjdGVkIHN1YnNldCBvZiB0aGUgZGF0YQphZmZlY3RlZENvdW50RGF0YSA8LSBub3JtYWxpemVkQ291bnREYXRhW2dyZXAoY29sbmFtZXMobm9ybWFsaXplZENvdW50RGF0YSksIHBhdHRlcm4gPSAiX0FMXyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXVssIGMoUEFUSUVOVF9TVEFSVDpQQVRJRU5UX0VORCwgKFBBVElFTlRfU1RBUlQgKyAyNCk6KFBBVElFTlRfRU5EICsgMjQpKV0KYWZmZWN0ZWRHcm91cHMgPC0gZ3JvdXBzW2dyb3VwcyRTYW1wbGUudHlwZSA9PSAiQUwiLAogICAgICAgICAgICAgICAgICAgICAgICAgXVtjKFBBVElFTlRfU1RBUlQ6UEFUSUVOVF9FTkQsIChQQVRJRU5UX1NUQVJUICsgMjQpOihQQVRJRU5UX0VORCArIDI0KSksIF0KCiMgQnVpbGQgdGhlIE1EUyBwbG90LCBjb2xvdXJpbmcgYnkgdGltZSAocHJlLSBhbmQgcG9zdC10cmVhdG1lbnQpCmRnZWxpc3QgPC0gZWRnZVI6OkRHRUxpc3QoY291bnRzID0gYWZmZWN0ZWRDb3VudERhdGEsIGdyb3VwID0gYWZmZWN0ZWRHcm91cHMkU2FtcGxlLnRpbWUpCmVkZ2VSOjpwbG90TURTLkRHRUxpc3QoZGdlbGlzdCwgbGFiZWxzID0gcm93bmFtZXMoYWZmZWN0ZWRHcm91cHMpLAogICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGMoImRhcmtncmVlbiIsICJibHVlIilbZmFjdG9yKGFmZmVjdGVkR3JvdXBzJFNhbXBsZS50aW1lKV0pCmxlZ2VuZCh4ID0gImJvdHRvbWxlZnQiLCBsZWdlbmQgPSBjKCJQcmUtdHJlYXRtZW50IiwgIlBvc3QtdHJlYXRtZW50IiksIGZpbGwgPSBjKCJkYXJrZ3JlZW4iLCAiYmx1ZSIpKQp0aXRsZShtYWluID0gIkRpc3RhbmNlIGJldHdlZW4gc2FtcGxlcywgY29sb3VyZWQgYnkgdHJlYXRtZW50IHRpbWUiKQpgYGAKCkFsc28gdHJ5IGdyb3VwaW5nIGJ5IHBhdGllbnQgdG8gc2VlIGFwcHJveGltYXRlbHkgaG93IG11Y2ggdmFyaWFiaWxpdHkgdGhlcmUgaXMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgQ29sb3VyIGJ5IHBhdGllbnQgbnVtYmVyCnBhdGllbnRDb2xvdXJzIDwtIHJhaW5ib3coUEFUSUVOVF9FTkQgLSBQQVRJRU5UX1NUQVJUICsgMSkKZWRnZVI6OnBsb3RNRFMuREdFTGlzdChkZ2VsaXN0LCBsYWJlbHMgPSByb3duYW1lcyhhZmZlY3RlZEdyb3VwcyksCiAgICAgICAgICAgICAgICAgICAgICAgY29sID0gcGF0aWVudENvbG91cnMpCnRpdGxlKG1haW4gPSAiRGlzdGFuY2UgYmV0d2VlbiBzYW1wbGVzLCBjb2xvdXJlZCBieSBwYXRpZW50IG51bWJlciIpCmBgYAoKV2Ugc2VlIHRoYXQgdGhlcmUgaXMgbm8gZXNwZWNpYWxseSBvYnZpb3VzIGNsdXN0ZXJpbmcgaW4gdHJlYXRtZW50IHRpbWUgb3IgaW5kaXZpZHVhbCBwYXRpZW50LCB3aGljaCBjb3VsZCBiZSBleHBsYWluZWQKYnkgdGhlIGltcGVyZmVjdCBkYXRhOyBlLmcuLCBwZXJoYXBzIHRoZSBwYXRpZW50cyBzaG93biBoZXJlIHVzZWQgZGlmZmVyZW50IGRydWdzIG9yIHNvbWUgd2VyZSBhY3R1YWxseSBjb250cm9scy4KUmVnYXJkbGVzcywgdGhlcmUgaXMgaW5jZW50aXZlIHRvIHRyeSBmaXR0aW5nIHRoZSBkYXRhIHRvIGEgbGluZWFyIG1vZGVsIHRvIHNlZSBpZiBhbnkgaW1wcm92ZW1lbnRzIGNhbiBiZSBmb3VuZC4KCmVkZ2VSIHByb3ZpZGVzIGEgY29udmVuaWVudCBxdWFzaS1saWtlbGlob29kIG1ldGhvZCB0aGF0IGhhcyBiZWVuIHJlY29tbWVuZGVkIGZvciBidWxrIFJOQS1TZXEgZXhwZXJpbWVudHMgYW5kIGlzIG9mdGVuCnVzZWQgZm9yIG1vcmUgY29tcGxpY2F0ZWQgbW9kZWxzLiBTaW5jZSB0aGUgZGF0YXNldCB0aGF0IHdlIGFyZSB3b3JraW5nIHdpdGggaXMgcXVpdGUgbGFyZ2UgYW5kIGNvbXBsZXgsIGl0IG1heSBiZSBhCmdvb2QgY2FuZGlkYXRlIHRvIHVzZS4gV2Ugd2lsbCBmaXJzdCB0cnkgYSBzdHJhaWdodGZvcndhcmQgbW9kZWwgdGhhdCBkb2VzIG5vdCBhY2NvdW50IGZvciBwYXRpZW50IHZhcmlhYmlsaXR5LgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIENyZWF0ZSBhIGxpbmVhciBkZXNpZ24gbWF0cml4IGZvciBlZGdlUiBxdWFzaSBsaWtlbGlob29kIGluIHRyZWF0bWVudCB0aW1lCm1vZGVsRGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+IGdyb3VwcyRTYW1wbGUudGltZSkKZGdlbGlzdCA8LSBlZGdlUjo6REdFTGlzdChjb3VudHMgPSBub3JtYWxpemVkQ291bnREYXRhLCBncm91cCA9IGdyb3VwcyRTYW1wbGUudGltZSkKZGdlbGlzdCA8LSBlZGdlUjo6ZXN0aW1hdGVEaXNwKGRnZWxpc3QsIG1vZGVsRGVzaWduKQptb2RlbEZpdCA8LSBlZGdlUjo6Z2xtUUxGaXQoZGdlbGlzdCwgbW9kZWxEZXNpZ24pCgojIEV4dHJhY3QgdGhlIHJlc3VsdHMKcWxmVGltZSA8LSBlZGdlUjo6Z2xtUUxGVGVzdChtb2RlbEZpdCwgY29lZiA9ICJncm91cHMkU2FtcGxlLnRpbWVtMyIpCnFsZkhpdHMgPC0gZWRnZVI6OnRvcFRhZ3MocWxmVGltZSwgc29ydC5ieSA9ICJQVmFsdWUiLCBuID0gbnJvdyhub3JtYWxpemVkQ291bnREYXRhKSkKa25pdHI6OmthYmxlKGVkZ2VSOjp0b3BUYWdzKHFsZlRpbWUpWzE6OCwgXSwgdHlwZSA9ICJodG1sIikKYGBgCgpOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIHRoZSB0aHJlc2hvbGQgcC12YWx1ZSAoMC4wNSk6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzJHRhYmxlJFBWYWx1ZSA8IDAuMDUpKWAgIApOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIGNvcnJlY3Rpb24gKEZEUik6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzJHRhYmxlJEZEUiA8IDAuMDUpKWAKClNpbmNlIHdlIGFyZSBkb2luZyBtYW55IHRlc3RzLCB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIHBvc2l0aXZlIHJlc3VsdCB3aWxsIG9jY3VyIHNpbXBseSBieSBjaGFuY2UgaXMgaW5jcmVhc2VkLiBUbwpjb250cm9sIGZvciB0aGlzLCB3ZSBjYW4gdXNlIGEgbWV0aG9kIG9mIG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdGluZywgd2hpY2ggd2lsbCB0cnkgdG8gY29ycmVjdCB0aGUgcG9zaXRpdml0eSByYXRlLgplZGdlUidzIHF1YXNpLWxpa2VsaWhvb2QgbWV0aG9kIGRvZXMgdGhpcyBieSB1c2luZyBmYWxzZSBkaXNjb3ZlcnkgcmF0ZSwgYSBmYWlybHkgY29tbW9uIHdheSB0byBjb3JyZWN0IHRoZSBkYXRhLgoKV2Ugc2VlIHRoYXQgYSBmYWlybHkgbGFyZ2UgbnVtYmVyIG9mIGdlbmVzIGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQsIGV2ZW4gYWZ0ZXIgY29ycmVjdGlvbiB3aXRoIEZEUiwgc28gYW4gZXZlbgpzdHJvbmdlciB0aHJlc2hvbGQgY2FuIGJlIGNvbnNpZGVyZWQuIFRoaXMgd2lsbCBhbGxvdyBmb3IgZ3JlYXRlciBsZXZlbHMgb2YgY29uZmlkZW5jZSB3aGVuIGFuYWx5emluZyB0aGUgZGF0YSwgYW5kCndpbGwgYWxzbyBwdXQgYSBncmVhdGVyIGVtcGhhc2lzIG9uIHdoaWNoIGdlbmVzIGNhbiBiZSBjb25zaWRlcmVkIHRvIGJlIG1vcmUgaW1wb3J0YW50IGJlY2F1c2UgdGhleSBhcmUgc2lnbmlmaWNhbnRseQpkaWZmZXJlbnRpYWxseSBleHByZXNzZWQuCgpOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIGEgc3RyaWN0ZXIgcC12YWx1ZSAoMC4wMSk6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzJHRhYmxlJFBWYWx1ZSA8IDAuMDEpKWAgIApOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIGNvcnJlY3Rpb24gKEZEUik6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzJHRhYmxlJEZEUiA8IDAuMDEpKWAKCkV2ZW4gc3RyaWN0ZXIgdGhyZXNob2xkcyBjb3VsZCBiZSBjb25zaWRlcmVkIChzdWNoIGFzIDAuMDAxKSwgYnV0IHdlIGRvIG5vdCB3YW50IHRvIGNvbXBsZXRlbHkgdGhyb3cgYXdheSB0aGUgd2Vha2VyCnNpZ25hbC4gV2UgY2FuIG5leHQgdHJ5IHRvIGZpdCB0aGUgZGF0YSB0byBhIG1vZGVsIHRoYXQgaW5jb3Jwb3JhdGVzIHBhdGllbnQgbnVtYmVyIGludG8gaXQsIHdoaWNoIG1heSBoYXZlIGJldHRlcgpyZXN1bHRzIGluIGRvd25zdHJlYW0gYW5hbHlzaXMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgRXNzZW50aWFsbHkgdGhlIHNhbWUgYXMgYmVmb3JlLCBidXQgd2l0aCBhZGRpdGlvbmFsIGRhdGEKbW9kZWxEZXNpZ25QYXRpZW50IDwtIG1vZGVsLm1hdHJpeCh+IGdyb3VwcyRQYXRpZW50ICsgZ3JvdXBzJFNhbXBsZS50aW1lKQpkZ2VsaXN0IDwtIGVkZ2VSOjpER0VMaXN0KGNvdW50cyA9IG5vcm1hbGl6ZWRDb3VudERhdGEsIGdyb3VwID0gZ3JvdXBzJFNhbXBsZS50aW1lKQpkZ2VsaXN0IDwtIGVkZ2VSOjplc3RpbWF0ZURpc3AoZGdlbGlzdCwgbW9kZWxEZXNpZ25QYXRpZW50KQptb2RlbEZpdCA8LSBlZGdlUjo6Z2xtUUxGaXQoZGdlbGlzdCwgbW9kZWxEZXNpZ25QYXRpZW50KQoKcWxmVGltZSA8LSBlZGdlUjo6Z2xtUUxGVGVzdChtb2RlbEZpdCwgY29lZiA9ICJncm91cHMkU2FtcGxlLnRpbWVtMyIpCnFsZkhpdHNQYXRpZW50IDwtIGVkZ2VSOjp0b3BUYWdzKHFsZlRpbWUsIHNvcnQuYnkgPSAiUFZhbHVlIiwgbiA9IG5yb3cobm9ybWFsaXplZENvdW50RGF0YSkpCmtuaXRyOjprYWJsZShlZGdlUjo6dG9wVGFncyhxbGZUaW1lWzE6OCwgXSksIHR5cGUgPSAiaHRtbCIpCmBgYAoKTnVtYmVyIG9mIGdlbmVzIHRoYXQgcGFzcyB0aGUgdGhyZXNob2xkIHAtdmFsdWUgKDAuMDUpOiBgciBsZW5ndGgod2hpY2gocWxmSGl0c1BhdGllbnQkdGFibGUkUFZhbHVlIDwgMC4wNSkpYCAgCk51bWJlciBvZiBnZW5lcyB0aGF0IHBhc3MgY29ycmVjdGlvbiAoRkRSKTogYHIgbGVuZ3RoKHdoaWNoKHFsZkhpdHNQYXRpZW50JHRhYmxlJEZEUiA8IDAuMDUpKWAKCk51bWJlciBvZiBnZW5lcyB0aGF0IHBhc3MgYSBzdHJpY3RlciBwLXZhbHVlICgwLjAxKTogYHIgbGVuZ3RoKHdoaWNoKHFsZkhpdHNQYXRpZW50JHRhYmxlJFBWYWx1ZSA8IDAuMDEpKWAgIApOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIGNvcnJlY3Rpb24gKEZEUik6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzUGF0aWVudCR0YWJsZSRGRFIgPCAwLjAxKSlgCgpXaGVuIGFwcGx5aW5nIGEgbW9kZWwgdGhhdCB0YWtlcyBwYXRpZW50IHZhcmlhYmlsaXR5IGludG8gYWNjb3VudCwgd2Ugc2VlIHRoYXQgdGhlIG51bWJlciBvZiBnZW5lcyB0aGF0IGFyZQpzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbmNyZWFzZXMsIHdoaWNoIGlzIG5vdCB1bmV4cGVjdGVkLgoKIyMjIEdlbmVzIG9mIGludGVyZXN0CgpBIHF1aWNrIG5leHQgc3RlcCBpcyB0byB0cnkgYW5kIGZpbmQgZ2VuZXMgb2YgaW50ZXJlc3Q7IGUuZy4sIHRob3NlIHRoYXQgYXJlIHRoZSBtb3N0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZC4gT25lCm1ldGhvZCBvZiBhY2NvbXBsaXNoaW5nIHRoaXMgdGFzayBpcyB0byB1c2UgYSB2b2xjYW5vIHBsb3QsIHdoaWNoIHB1dHMgcC12YWx1ZSBhZ2FpbnN0IGxvZy1mb2xkIGNoYW5nZS4gSW4gdGhpcyB3YXksCmdlbmVzIHdpdGggdGhlIG1vc3QgbWVhbmluZ2Z1bCBvciBzaWduaWZpY2FudCBjaGFuZ2VzIHdpbGwgYmUgZm91bmQgbmVhciB0aGUgdG9wLWxlZnQgYW5kIHRvcC1yaWdodCBjb3JuZXJzIG9mIHRoZQpncmFwaC4gSGVyZSwgdGhlIFIgcGFja2FnZSBFbmhhbmNlZFZvbGNhbm8gaXMgdXNlZCB0byBxdWlja2x5IHRocm93IHRvZ2V0aGVyIHN1Y2ggYSBwbG90IFtAZW5oYW5jZWR2b2xjYW5vXS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBHZXQgdGhlIGxvZy1mb2xkIGNoYW5nZSBhbmQgcC12YWx1ZXMgZnJvbSB0aGUgcGF0aWVudC12YXJpYWJpbGl0eS1pbmNsdWRlZCBtb2RlbAp2b2xjYW5vIDwtIGRhdGEuZnJhbWUobG9nMkZDID0gcWxmVGltZSR0YWJsZSRsb2dGQywgUFZhbHVlID0gcWxmVGltZSR0YWJsZSRQVmFsdWUpCnJvd25hbWVzKHZvbGNhbm8pIDwtIHJvd25hbWVzKG5vcm1hbGl6ZWRDb3VudERhdGEpCkVuaGFuY2VkVm9sY2Fubzo6RW5oYW5jZWRWb2xjYW5vKHZvbGNhbm8sIGxhYiA9IHJvd25hbWVzKHZvbGNhbm8pLCB4ID0gImxvZzJGQyIsIHkgPSAiUFZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiRGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIikKYGBgCgpXZSBzZWUgdGhhdCB0aGUgdmFzdCBtYWpvcml0eSBvZiBnZW5lcyAoMTcsNzQ3IGluIHRvdGFsKSBhcmUgbm90IHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLCBiZWluZyBwbG90dGVkCnRvd2FyZCB0aGUgYm90dG9tLWNlbnRyZSBvZiB0aGUgZ3JhcGgsIGJ1dCB0aGVyZSBhcmUgYSBmZXcgZ2VuZXMgdGhhdCBwcmVzZW50IHRoZW1zZWx2ZXMgYXMgb2YgaW50ZXJlc3QuIEZvciBleGFtcGxlLAp0aGUgcGxvdCBpZGVudGlmaWVzIENDTDE4LCBJTDE5LCBOUFRYMiwgQURPUkEzLCBBTE9YMTUsIFNQQVJDLCBBQ1NNNiwgYW5kIFNQSU5LOSBhcyBnZW5lcyB3aXRoIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzCmluIGxldmVscyBvZiBleHByZXNzaW9uLgoKVGhlIG5leHQgc3RlcCBpcyB0byByZXR1cm4gdG8gbG9va2luZyBhdCBoZWF0bWFwczsgd2UgY2FuIG5vdyBwbG90IHRoZSByZXN1bHRzIG9mIGFwcGx5aW5nIHRoZSBxdWFzaS1saWtlbGlob29kIG1vZGVsCnRvIHRoZSBkYXRhLCB3aGljaCBtYXkgcmVzdWx0IGluIGNsZWFyZXIgY2x1c3RlcnMgdGhhdCBjYW4gYmUgdmlzdWFsbHkgaWRlbnRpZmllZC4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBVc2luZyB0aGUgc3RyaWN0ZXIgcC12YWx1ZSB0aHJlc2hvbGQKdG9wSGl0cyA8LSByb3duYW1lcyhxbGZIaXRzJHRhYmxlKVtxbGZIaXRzJHRhYmxlJFBWYWx1ZSA8IDAuMDFdCiMgV2FybmluZzogaGVhdG1hcE1hdHJpeCBtYXkgb25seSBoYXZlIGJlZW4gYnVpbHQgd2l0aCBhIHN1YnNldCBvZiB0aGUgZGF0YS4gdG9wSGVhdG1hcE1hdHJpeCBoZXJlIHdpbGwgcmVmbGVjdCB0aGF0CiMgc3Vic2V0LiBDaGFuZ2UgU0FNUExFX1NUQVJUIGFuZCBTQU1QTEVfRU5EIGluIHRoZSBjb2RlIHNuaXBwZXQgYXQgdGhlIHN0YXJ0IG9mIHRoaXMgc2VjdGlvbiB0byBjaGFuZ2UgdGhlIHNlbGVjdGVkCiMgZGF0YQp0b3BIZWF0bWFwTWF0cml4IDwtIHQoc2NhbGUodChoZWF0bWFwTWF0cml4W3doaWNoKHJvd25hbWVzKGhlYXRtYXBNYXRyaXgpICVpbiUgdG9wSGl0cyksIF0pKSkKdG9wSGVhdG1hcE1hdHJpeFtpcy5uYSh0b3BIZWF0bWFwTWF0cml4KV0gPC0gMAoKaWYgKG1pbih0b3BIZWF0bWFwTWF0cml4KSA9PSAwKSB7CiAgaGVhdG1hcENvbG91cnMgPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygwLCBtYXgodG9wSGVhdG1hcE1hdHJpeCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewogIGhlYXRtYXBDb2xvdXJzIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMobWluKHRvcEhlYXRtYXBNYXRyaXgpLCAwLCBtYXgodG9wSGVhdG1hcE1hdHJpeCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KCmdyb3VwQ29sb3VycyA8LSBjKCJibHVlIiwgInJlZCIpCm5hbWVzKGdyb3VwQ29sb3VycykgPC0gdW5pcXVlKGdyb3VwcyRTYW1wbGUudHlwZSkKaGVhdG1hcEFubm8gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKGRmID0gZGF0YS5mcmFtZShUeXBlID0gZ3JvdXBzJFNhbXBsZS50eXBlW1NBTVBMRV9TVEFSVDpTQU1QTEVfRU5EXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KHR5cGUgPSBncm91cENvbG91cnMpKQoKY3VyckhlYXRtYXAgPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAoYXMubWF0cml4KHRvcEhlYXRtYXBNYXRyaXgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGhlYXRtYXBDb2xvdXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJLZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiVG9wIGhpdHMgaGVhdG1hcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGVhdG1hcEFubm8pCgpjdXJySGVhdG1hcApgYGAKClRoaXMgaGVhdG1hcCBpcyBub3QgcGFydGljdWxhcmx5IHVzZWZ1bCBpbiBkZXRlcm1pbmluZyBpZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgY2x1c3RlciwgYmVjYXVzZSB0aGUgY29sdW1ucwphcmUgb3JkZXJlZCBzdWNoIHRoYXQgaW5kaXZpZHVhbCBwYXRpZW50IGRhdGEgaXMgcGFpcmVkIHRvZ2V0aGVyLiBGb3IgZXhhbXBsZSwgdGhlIGZpcnN0IGNvbHVtbiBoZXJlIGlzIHRoZSBjb250cm9sCnNhbXBsZSBmb3IgdGhlIGZpcnN0IHBhdGllbnQgKHRoZSBub24tYWZmZWN0ZWQgc2tpbikgcHJlLXRyZWF0bWVudCwgYW5kIHRoZSBzZWNvbmQgY29sdW1uIGlzIHRoZSBhZmZlY3RlZCBza2luIHNhbXBsZSwKYWxzbyBwcmUtdHJlYXRtZW50LiBJdCBzaG91bGQgYmUgcG9zc2libGUgdG8gZ2V0IGEgYmV0dGVyIGlkZWEgb2YgY2x1c3RlcmluZyBvdmVyIHRpbWUgYnkgcmVvcmRlcmluZyB0aGUgY29sdW1ucy4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBCdWlsZCBhIG5ldyBoZWF0bWFwIG1hdHJpeCB0byBkZWNvdXBsZSBmcm9tIFNBTVBMRV9TVEFSVCBhbmQgU0FNUExFX0VORApyZUhlYXRtYXBNYXRyaXggPC0gbm9ybWFsaXplZENvdW50RGF0YQpyZUhlYXRtYXBNYXRyaXggPC0gdChzY2FsZSh0KHJlSGVhdG1hcE1hdHJpeCkpKQpyZUhlYXRtYXBNYXRyaXhbaXMubmEocmVIZWF0bWFwTWF0cml4KV0gPC0gMAoKIyBEb24ndCBrbm93IGhvdyB0byBkbyB0aGlzIGluIGEgb25lLWxpbmVyCm5ld0lkeHMgPC0gaW50ZWdlcihTQU1QTEVfRU5EIC0gU0FNUExFX1NUQVJUICsgMSkKY3VycklkeCA8LSAxCiMgQWRkIHRoZSAqX0FOX20wIGZpcnN0CmZvciAoaSBpbiBzZXEoU0FNUExFX1NUQVJULCBTQU1QTEVfRU5ELCAyKSkgewogIG5ld0lkeHNbY3VycklkeF0gPC0gaQogIGN1cnJJZHggPC0gY3VycklkeCArIDEKfQojIFRoZW4gYWRkIHRoZSAqX0FOX20zCmZvciAoaSBpbiBzZXEoU0FNUExFX1NUQVJULCBTQU1QTEVfRU5ELCAyKSkgewogIG5ld0lkeHNbY3VycklkeF0gPC0gaSArIDQ4CiAgY3VycklkeCA8LSBjdXJySWR4ICsgMQp9Cgp0b3BIZWF0bWFwTWF0cml4IDwtIHQoc2NhbGUodChyZUhlYXRtYXBNYXRyaXhbd2hpY2gocm93bmFtZXMocmVIZWF0bWFwTWF0cml4KSAlaW4lIHRvcEhpdHMpLCBdKSkpCnRvcEhlYXRtYXBNYXRyaXhbaXMubmEodG9wSGVhdG1hcE1hdHJpeCldIDwtIDAKdG9wSGVhdG1hcE1hdHJpeCA8LSB0b3BIZWF0bWFwTWF0cml4WywgbmV3SWR4c10KCmdyb3VwQ29sb3VycyA8LSBjKCJibHVlIiwgInJlZCIpCm5hbWVzKGdyb3VwQ29sb3VycykgPC0gdW5pcXVlKGdyb3VwcyRTYW1wbGUudGltZSkKaGVhdG1hcEFubm8gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKGRmID0gZGF0YS5mcmFtZShUaW1lID0gZ3JvdXBzJFNhbXBsZS50aW1lW25ld0lkeHNdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3QodHlwZSA9IGdyb3VwQ29sb3VycykpCgpjdXJySGVhdG1hcCA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChhcy5tYXRyaXgodG9wSGVhdG1hcE1hdHJpeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gaGVhdG1hcENvbG91cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIktleSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90aXRsZSA9ICJUb3AgaGl0cywgY29udHJvbCwgb3ZlciB0aW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoZWF0bWFwQW5ubykKCmN1cnJIZWF0bWFwCmBgYAoKVGhlIGFib3ZlIGhlYXRtYXAgaGFzIGJlZW4gcmVhcnJhbmdlZCBzdWNoIHRoYXQgYWxsIGVpZ2h0IGNvbHVtbnMgYXJlIGNvbnRyb2wgKG5vbi1hZmZlY3RlZC9ub24tbGVzaW9uYWwpIHNraW4gb2YKYWZmZWN0ZWQgcGF0aWVudHMsIHdpdGggdGhlIGxlZnQgZm91ciBjb2x1bW5zIGJlaW5nIHNhbXBsZXMgdGFrZW4ganVzdCBwcmlvciB0byB0cmVhdG1lbnQsIGFuZCB0aGUgcmlnaHQgZm91ciBzYW1wbGVzCmJlaW5nIHRha2VuIGFmdGVyIHRocmVlIG1vbnRocyBvZiB0cmVhdG1lbnQuIFNpbmNlIHRoaXMgc3Vic2V0IG9mIHRoZSBkYXRhc2V0IGRvZXMgbm90IGNvbnRhaW4gYW55IHNhbXBsZXMgb2YgYWZmZWN0ZWQKKGxlc2lvbmFsKSBza2luLCBpdCBpcyBub3Qgc3VycHJpc2luZyB0aGF0IGxldmVscyBvZiBnZW5lIGV4cHJlc3Npb24gYXJlIHNpbWlsYXIgYWNyb3NzIHRoZSB0d28gY2F0ZWdvcmllcy4gVGhlIG1vc3QKb2J2aW91cyBkaWZmZXJlbmNlIHdvdWxkIGJlIHdpdGggcmVzcGVjdCB0byBwYXRpZW50IDMgKGFuZCBwYXRpZW50IDEsIHRvIGEgY2VydGFpbiBleHRlbnQpLCB3aGVyZSB0aGUgZm9sbG93LXVwIHNhbXBsZQpoYXMgZ3JlYXRlciBsZXZlbHMgb2YgcmVndWxhdGlvbi0tYnV0IGl0IGlzIHN0aWxsIG9mIHRoZSBzYW1lIHR5cGUuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgRG9pbmcgdGhlIHNhbWUgYXMgYWJvdmUgZXhjZXB0IHdpdGggKl9BTF8qIGluc3RlYWQgb2YgKl9BTl8qCm5ld0lkeHMgPC0gaW50ZWdlcihTQU1QTEVfRU5EIC0gU0FNUExFX1NUQVJUICsgMSkKY3VycklkeCA8LSAxCmZvciAoaSBpbiBzZXEoU0FNUExFX1NUQVJUICsgMSwgU0FNUExFX0VORCArIDEsIDIpKSB7CiAgbmV3SWR4c1tjdXJySWR4XSA8LSBpCiAgY3VycklkeCA8LSBjdXJySWR4ICsgMQp9CmZvciAoaSBpbiBzZXEoU0FNUExFX1NUQVJUICsgMSwgU0FNUExFX0VORCArIDEsIDIpKSB7CiAgbmV3SWR4c1tjdXJySWR4XSA8LSBpICsgNDgKICBjdXJySWR4IDwtIGN1cnJJZHggKyAxCn0KCnRvcEhlYXRtYXBNYXRyaXggPC0gdChzY2FsZSh0KHJlSGVhdG1hcE1hdHJpeFt3aGljaChyb3duYW1lcyhyZUhlYXRtYXBNYXRyaXgpICVpbiUgdG9wSGl0cyksIF0pKSkKdG9wSGVhdG1hcE1hdHJpeFtpcy5uYSh0b3BIZWF0bWFwTWF0cml4KV0gPC0gMAp0b3BIZWF0bWFwTWF0cml4IDwtIHRvcEhlYXRtYXBNYXRyaXhbLCBuZXdJZHhzXQoKZ3JvdXBDb2xvdXJzIDwtIGMoImJsdWUiLCAicmVkIikKbmFtZXMoZ3JvdXBDb2xvdXJzKSA8LSB1bmlxdWUoZ3JvdXBzJFNhbXBsZS50aW1lKQpoZWF0bWFwQW5ubyA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oZGYgPSBkYXRhLmZyYW1lKFRpbWUgPSBncm91cHMkU2FtcGxlLnRpbWVbbmV3SWR4c10pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdCh0eXBlID0gZ3JvdXBDb2xvdXJzKSkKCmN1cnJIZWF0bWFwIDwtIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKGFzLm1hdHJpeCh0b3BIZWF0bWFwTWF0cml4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBoZWF0bWFwQ29sb3VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiS2V5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIlRvcCBoaXRzLCBvdmVyIHRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhlYXRtYXBBbm5vKQoKY3VyckhlYXRtYXAKYGBgCgpXaGVuIHdlIGxvb2sgYXQgdGhlIGhlYXRtYXAgY29uY2VybmluZyBhZmZlY3RlZCBza2luLCB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaXMgbW9yZSBvYnZpb3VzIGFuZCBtb3JlIHNpZ25pZmljYW50LgpUaGVyZSBhcmUgbWFueSBjbHVzdGVycyBvZiBnZW5lcyBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgcGxvdCB0aGF0IGNvcnJlc3BvbmQgaW52ZXJzZWx5IHRvIHRoZSBzYW1lIHJlZ2lvbiBvbiB0aGUgbGVmdApzaWRlIG9mIHRoZSBwbG90LiBGb3IgZXhhbXBsZSwgdGhlIGNvbHVtbiBvZiBwYXRpZW50IDcgaGFzIGEgbGFyZ2UsIHN0cm9uZ2x5IGV4cHJlc3NlZCBncm91cCBvZiBnZW5lcyBuZWFyIHRoZSBtaWRkbGUKb2YgdGhlIGNvbHVtbi4gSG93ZXZlciwgd2hlbiBsb29raW5nIGF0IHRoZSBzYW1lIGFyZWEgb24gdGhlIGxlZnQgc2lkZSBvZiB0aGUgaGVhdG1hcCwgdGhlIGV4cHJlc3Npb24gb2YgdGhlIHNhbWUgZ2VuZXMKaXMgdmVyeSB3ZWFrIGluIGNvbXBhcmlzb24uIFRodXMsIHdlIGNhbiBoeXBvdGhlc2l6ZSB0aGF0IHBlcmhhcHMgdGhlc2UgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIG1heSBiZQpyZXNwb25zaWJsZSBmb3IgQUQgYW5kL29yIGl0cyBzeW1wdG9tcy4KCiMjIDMuIFRocmVzaG9sZGVkIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMKCkluIHRoaXMgbmV4dCBwYXJ0IG9mIHRoZSBub3RlYm9vaywgdGhlIHNldHMgb2Ygc2lnbmlmaWNhbnRseSB1cC1yZWd1bGF0ZWQgYW5kIGRvd24tcmVndWxhdGVkIGdlbmVzIHdpbGwgYmUgcnVuIHRocm91Z2gKYSB0aHJlc2hvbGRlZCBnZW5lIHNldCBlbnJpY2htZW50IGFuYWx5c2lzLiBUaGUgZmlyc3Qgc3RlcCBpcyB0byBnZXQgdGhlIGxpc3RzIG9mIHN1Y2ggZ2VuZXMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgQ29uc3RydWN0IHRoZSByYW5rZWQgZ2VuZSBsaXN0IChub24tdGhyZXNob2xkZWQpCnFsZkhpdHNUIDwtIGFzLmRhdGEuZnJhbWUocWxmSGl0c1BhdGllbnQpCnFsZkhpdHNUWyJyYW5rIl0gPC0gLWxvZyhxbGZIaXRzVCRQVmFsdWUsIGJhc2UgPSAxMCkgKiBzaWduKHFsZkhpdHNUJGxvZ0ZDKQpxbGZIaXRzVCA8LSBxbGZIaXRzVFtvcmRlcihxbGZIaXRzVCRyYW5rKSwgXQoKd3JpdGUudGFibGUoZGF0YS5mcmFtZShnZW5lbmFtZSA9IHJvd25hbWVzKHFsZkhpdHNUKSwgRnN0YXQgPSBxbGZIaXRzVCRyYW5rKSwKICAgICAgICAgICAgZmlsZSA9IGZpbGUucGF0aChnZXR3ZCgpLCAicmFua2VkR2VuZWxpc3QudHh0IiksCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSkKCiMgRm9yIHRoZSB0aHJlc2hvbGRlZCBsaXN0cywgdXNlIHAgPSAwLjA1IGluc3RlYWQgb2YgMC4wMSB0byBnZXQgbW9yZSByZXN1bHRzCnVwcmVnR2VuZXMgPC0gcm93bmFtZXMocWxmSGl0c1Rbd2hpY2gocWxmSGl0c1QkUFZhbHVlIDwgMC4wNSAmIHFsZkhpdHNUJGxvZ0ZDID4gMCksIF0pCndyaXRlLnRhYmxlKHVwcmVnR2VuZXMsCiAgICAgICAgICAgIGZpbGUgPSBmaWxlLnBhdGgoZ2V0d2QoKSwgInVwcmVndWxhdGVkR2VuZXMudHh0IiksCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSkKCmRvd25yZWdHZW5lcyA8LSByb3duYW1lcyhxbGZIaXRzVFt3aGljaChxbGZIaXRzVCRQVmFsdWUgPCAwLjA1ICYgcWxmSGl0c1QkbG9nRkMgPCAwKSwgXSkKd3JpdGUudGFibGUoZG93bnJlZ0dlbmVzLAogICAgICAgICAgICBmaWxlID0gZmlsZS5wYXRoKGdldHdkKCksICJkb3ducmVndWxhdGVkR2VuZXMudHh0IiksCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSkKYGBgCgpUaGUgdGVjaG5pcXVlIHRoYXQgaGFzIGJlZW4gY2hvc2VuIHRvIHBlcmZvcm0gdGhpcyBhbmFseXNpcyBpcyBGaXNoZXIncyBleGFjdCB0ZXN0LCBkb25lIHRocm91Z2ggZzpQcm9maWxlcgpbQGdwcm9maWxlcl0uIEZpc2hlcidzIGV4YWN0IHRlc3QgaXMgYSBjb21tb24gc3RhdGlzdGljYWwgbWV0aG9kIHRvIGNoZWNrIGZvciBvdmVycmVwcmVzZW50YXRpb24sIHdoaWNoIGlzIHRoZSBnb2FsIG9mCnRoaXMgYW5hbHlzaXMsIGFuZCBpcyBhYmxlIHRvIGNhbGN1bGF0ZSB0aGUgZGV2aWF0aW9uIGZyb20gcC12YWx1ZXMgZXhhY3RseS4gZzpQcm9maWxlciBpcyBhIGNvbW1vbiwgY29udmVuaWVudAooYWNjZXNzaWJsZSBvbmxpbmUgYW5kIHRocm91Z2ggYW4gUiBwYWNrYWdlKSBpbXBsZW1lbnRhdGlvbiBvZiBGaXNoZXIncyBleGFjdCB0ZXN0IHRoYXQgaXMgdXBkYXRlZCBmYWlybHkgcmVndWxhcmx5LAp3aGljaCBjYW4gaGVscCBpbmNyZWFzZSB0aGUgYWNjdXJhY3kgb2YgdGhlIGFuYWx5c2lzLiBGb3Igb3VyIHB1cnBvc2UsIGFuZCBpbiB0aGUgaW50ZXJlc3Qgb2YgdGltZSwgZzpQcm9maWxlciB3aWxsIGJlCmFjY2Vzc2VkIHRocm91Z2ggaXRzIHdlYnNpdGUgKGluc3RlYWQgb2YgdXNpbmcgdGhlIFIgcGFja2FnZSkuCgpUaGUgYW5ub3RhdGlvbiBkYXRhc2V0cyB0aGF0IGhhdmUgYmVlbiBzZWxlY3RlZCBpbmNsdWRlIEdPIGJpb2xvZ2ljYWwgcHJvY2Vzc2VzICh1cGRhdGVkIDIwMjEtMTItMTUpIC0gd2l0aG91dAplbGVjdHJvbmljIGFubm90YXRpb25zLCBSZWFjdG9tZSAodXBkYXRlZCAyMDIyLTAxLTAzKSwgYW5kIFdpa2lQYXRod2F5cyAodXBkYXRlZCAyMDIxLTEyLTEwKS4gVGhlc2UgdGhyZWUgZGF0YXNldHMgYXJlCm1ham9yIGFubm90YXRpb24gcmVzb3VyY2VzLCBhbmQgaGF2ZSBiZWVuIHVwZGF0ZWQgZmFpcmx5IHJlY2VudGx5LCBzbyBpdCBpcyByZWFzb25hYmxlIHRvIGV4cGVjdCBhY2N1cmFjdGUgcmVzdWx0cy4KT3RoZXIgbWFqb3IgcmVzb3VyY2VzIHN1Y2ggYXMgS0VHRyBoYXZlIG5vdCBiZWVuIGluY2x1ZGVkIGJlY2F1c2UgdGhleSBtYXkgYWRkIGFkZGl0aW9uYWwgZXh0cmFuZW91cyBpbmZvcm1hdGlvbiB0byB0aGUKcmVzdWx0cywgYW5kIHdpdGggdGhlc2UgdGhyZWUgZGF0YXNldHMsIHRoZXJlIHNob3VsZCBhbHJlYWR5IGJlIGVub3VnaCBpbmZvcm1hdGlvbiBmb3IgYSB0aG9yb3VnaCBhbmFseXNpcy4KCldlIHdpbGwgc3RhcnQgYnkgbG9va2luZyBhdCBzdWJzZXRzIG9mIHRoZSBnZW5lcyBvZiBpbnRlcmVzdCAoaS5lLiwgdGhvc2Ugd2l0aCBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiksCmFuZCB0aGVuIGZvbGxvdyB1cCBieSBhbmFseXppbmcgdGhlIGVudGlyZSBzZXQgdG8gY29tcGFyZSBob3cgbG9va2luZyBhdCBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIGRhdGEgbWF5IGFmZmVjdCB0aGUKcmVzdWx0cy4gRm9yIGVhY2ggcnVuLCBGRFIgd2lsbCBiZSB1c2VkIGZvciBtdWx0aXBsZSBoeXBvdGhlc2lzIGNvcnJlY3Rpb24sIHdpdGggYSB0aHJlc2hvbGQgcC12YWx1ZSBvZiAwLjA1LiBUaGlzIHdhcwpjaG9zZW4gaW5zdGVhZCBvZiB0aGUgbW9yZSBzdHJpbmdlbnQgMC4wMSB0byBhbGxvdyBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhbmQgYSBicm9hZGVyIGxvb2sgYXQgdGhlIGFzc29jaWF0ZWQgcGF0aHdheXMuCkluIGFkZGl0aW9uLCB0aGUgcmVzdWx0aW5nIHBhdGh3YXlzIHdlcmUgZmlsdGVyZWQgdG8gb25seSBpbmNsdWRlIHRob3NlIHdpdGggZ2VuZSBzZXQgc2l6ZXMgb2YgMjAwIG9yIGxlc3MuIFRoaXMgaXMKYmVjYXVzZSBvZiB0aGUgaW5oZXJpdGFuY2UtbGlrZSBwcm9wZXJ0eSBvZiB0aGUgYW5ub3RhdGlvbiByZXNvdXJjZXMsIHdoaWNoIG1heSBjbHV0dGVyIHRoZSByZXN1bHRzIGJ5IGluY2x1ZGluZwpwYXRod2F5cyB0aGF0IGFyZSB0b28gZ2VuZXJpYy4KCiMjIyBVcHJlZ3VsYXRlZCBnZW5lcwoKVGhlIGZpcnN0IHN1YnNldCBpcyBhbGwgdXByZWd1bGF0ZWQgZ2VuZXMgd2l0aCBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gVGhlIGxpc3Qgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGZyb20KdGhlIGNvZGUgc25pcHBldCBhYm92ZSkgd2FzIGNvcGllZCB0byBnOlByb2ZpbGVyLCB0aGUgbWVudGlvbmVkIG9wdGlvbnMgd2VyZSBzZXQgKHdpdGggYWxsIHJlc3VsdHMgY2hlY2tlZCksIGFuZCB0aGUKYW5hbHlzaXMgd2FzIHJ1bi4gVGhlIHJlc3VsdCB3YXMgdGhlIGZvbGxvd2luZyBmaWd1cmU6CgohW0ZpZ3VyZSAxLiB1cHJlZ3VsYXRlZCBnZW5lcyBvbmx5XShmaWd1cmVzL2dwcm9mX3VwLnBuZykKCmc6UHJvZmlsZXIgaGFzIG9taXR0ZWQgdGl0bGVzIGZvciB0aGUgcGxvdCBhbmQgdGFibGUsIGJ1dCBhcHByb3ByaWF0ZSBvbmVzIHdvdWxkIGJlICJPdmVyLXJlcHJlc2VudGF0aW9uIG9mIHVwcmVndWxhdGVkCmdlbmVzIHdpdGggcC12YWx1ZSA8IDAuMDUiIGFuZCAiSGlnaGVzdCBzY29yaW5nIHBhdGh3YXlzIGZvciBnZW5lIHNldHMgd2l0aCBzaXplIGxlc3MgdGhhbiAyMDAiLCByZXNwZWN0aXZlbHkuCgpVc2luZyB0aGUgdGhyZXNob2xkcyBtZW50aW9uZWQgcHJldmlvdXNseSwgR086QlAgZm91bmQgNSwwMjcgcGF0aHdheXMsIFJFQUMgZm91bmQgMSwwMDcsIGFuZCBXUCBmb3VuZCA0NDUuIFRoZSBwbG90IGF0CnRoZSB0b3Agb2YgdGhlIGZpZ3VyZSBpcyBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiBhZGp1c3RlZCBwLXZhbHVlcyB2cy4gcmVzb3VyY2UsIHdoZXJlIGVhY2ggZG90IHJlcHJlc2VudHMgYQpzcGVjaWZpYyBwYXRod2F5IChlYWNoIGFubm90YXRpb24gcmVzb3VyY2UgaGFzIGl0cyBvd24gY29sb3VyKS4gVGhlIHRhYmxlIGF0IHRoZSBib3R0b20gb2YgdGhlIGZpZ3VyZSBpcyBhIGxpc3Qgb2YgdGhlCnRvcCAoYXBwcm94aW1hdGVseSAxNSkgcGF0aHdheXMsIGFjY29yZGluZyB0byBhZGp1c3RlZCBwLXZhbHVlLCBmb3IgZWFjaCByZXNvdXJjZS4gVGhlIGluZGl2aWR1YWwgcGF0aHdheXMgYXJlIGdpdmVuCm51bWJlcnMsIGFuZCB0aGVzZSBjb3JyZXNwb25kIHRvIHRoZSBsYWJlbGxpbmcgb2YgdGhlIGRvdHMgaW4gdGhlIHBsb3QgYWJvdmUuIFdlIHNlZSB0aGF0IGRlc3BpdGUgYmVpbmcgdGhlIHBhdGh3YXlzCndpdGggdGhlIGdyZWF0ZXN0IGFkanVzdGVkIHAtdmFsdWUsIHRoZXNlIGxhYmVsbGVkIGRvdHMgYXJlIG5vdCB0aGUgaGlnaGVzdCBvbiB0aGUgcGxvdCBiZWNhdXNlIG9mIHRoZSB0aHJlc2hvbGRzIHRoYXQKd2VyZSB1c2VkOyB0aGUgdW5sYWJlbGxlZCBkb3RzIHdvdWxkIHJlcHJlc2VudCBtb3JlIGdlbmVyaWMgcGF0aHdheXMuCgpUaGUgc3BlY2lmaWMgcGF0aHdheXMgdGhhdCB3ZXJlIGZvdW5kIGhlcmUtLWZvciBleGFtcGxlLCBjb2xsYWdlbiBmaWJyaWwgb3JnYW5pemF0aW9uIGFuZCBzZXZlcmFsIGZhdHR5IGFjaWQgbWV0YWJvbGljCnByb2Nlc3Nlcy0tYXJlIG5vdCBvYnZpb3VzbHkgaW5kaWNhdGl2ZSBvZiBBRCwgYSBza2luIGRpc2Vhc2Ugb2YgYXV0b2ltbXVuZSBvcmlnaW4uCgojIyMgRG93bnJlZ3VsYXRlZCBnZW5lcwoKVGhlIHNlY29uZCBzdWJzZXQgaXMgYWxsIGRvd25yZWd1bGF0ZWQgZ2VuZXMgd2l0aCBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gVGhlIGxpc3Qgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcwp3YXMgY29waWVkIHRvIGc6UHJvZmlsZXIsIG9wdGlvbnMgd2VyZSBzZXQgKHRoaXMgdGltZSB3aXRob3V0IGFsbCByZXN1bHRzLCBzaW5jZSB0aGUgbGlzdCBvZiBnZW5lcyB3YXMgdG9vIGxhcmdlKSwgYW5kCnRoZSBhbmFseXNpcyB3YXMgcnVuLiBUaGUgcmVzdWx0IHdhcyB0aGUgZm9sbG93aW5nIGZpZ3VyZToKCiFbRmlndXJlIDIuIGRvd25yZWd1bGF0ZWQgZ2VuZXMgb25seV0oZmlndXJlcy9ncHJvZl9kb3duLnBuZykKCmc6UHJvZmlsZXIgaGFzIG9taXR0ZWQgdGl0bGVzIGZvciB0aGUgcGxvdCBhbmQgdGFibGUsIGJ1dCBhcHByb3ByaWF0ZSBvbmVzIHdvdWxkIGJlICJPdmVyLXJlcHJlc2VudGF0aW9uIG9mCmRvd25yZWd1bGF0ZWQgZ2VuZXMgd2l0aCBwLXZhbHVlIDwgMC4wNSIgYW5kICJIaWdoZXN0IHNjb3JpbmcgcGF0aHdheXMgZm9yIGdlbmUgc2V0cyB3aXRoIHNpemUgbGVzcyB0aGFuIDIwMCIsCnJlc3BlY3RpdmVseS4KClVzaW5nIHRoZSB0aHJlc2hvbGRzIG1lbnRpb25lZCBwcmV2aW91c2x5LCBHTzpCUCBmb3VuZCAyMzAgcGF0aHdheXMsIFJFQUMgZm91bmQgNzIsIGFuZCBXUCBmb3VuZCA1Mi4gVGhlIHNwZWNpZmljCnBhdGh3YXlzIHRoYXQgd2VyZSBmb3VuZCBoZXJlLS1mb3IgZXhhbXBsZSwgdmFyaW91cyBETkEgcmVwbGljYXRpb24gcGF0aHdheXMgYW5kIGNlbGwgY3ljbGUgc2lnbmFsbGluZy0taXMsIGxpa2UgdGhlCnVwcmVndWxhdGVkIGdlbmVzLCBub3QgZGlyZWN0bHkgcmVsYXRlZCB0byBBRC4gSG93ZXZlciwgdGhlc2UgZXhhbXBsZXMgYXJlIG5vdCBhcyBicm9hZCwgYW5kIGl0IGlzIHBvc3NpYmxlIHRoYXQgdGhlcmUKaXMgYW4gaW5kaXJlY3QgbGluay4gRm9yIGV4YW1wbGUsIHBzb3JpYXNpcywgYW5vdGhlciBza2luIGNvbmRpdGlvbiBzaW1pbGFyIHRvIGF0b3BpYyBkZXJtYXRpdGlzLCBpcyBkaXJlY3RseSByZWxhdGVkCnRvIHRoZSBkaXNyZWd1bGF0aW9uIG9mIHRoZSBjZWxsIGN5Y2xlIG9mIHNraW4gY2VsbHMsIHNvIGl0IHdvdWxkIG5vdCBiZSBhIHN1cnByaXNlIHRvIHNlZSBhIHNpbWlsYXIgcmVzdWx0IGhlcmUuCgojIyMgQWxsIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwoKSXQgaXMgaW1wb3J0YW50IHRvIGNvbnNpZGVyIHRoZSBlbnRpcmUgc2V0IG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhcyBhIHdob2xlLCBiZWNhdXNlIGl0IGlzIHBvc3NpYmxlIHRoYXQKdGhlIHBhcnRpY3VsYXIgcGF0aHdheXMgdGhhdCB3ZSBhcmUgZXhwZWN0aW5nIGhhdmUgYm90aCB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcy4gVGhlIHJlc3VsdCBvZiBydW5uaW5nCmc6UHJvZmlsZXIgb24gdGhlIGxpc3Qgb2YgY29tYmluZWQgZ2VuZXMgaXMgYXMgZm9sbG93czoKCiFbRmlndXJlIDMuIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXNdKGZpZ3VyZXMvZ3Byb2ZfZGUucG5nKQoKZzpQcm9maWxlciBoYXMgb21pdHRlZCB0aXRsZXMgZm9yIHRoZSBwbG90IGFuZCB0YWJsZSwgYnV0IGFwcHJvcHJpYXRlIG9uZXMgd291bGQgYmUgIk92ZXItcmVwcmVzZW50YXRpb24gb2Ygc2lnbmlmaWNhbnQKZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIHdpdGggcC12YWx1ZSA8IDAuMDUiIGFuZCAiSGlnaGVzdCBzY29yaW5nIHBhdGh3YXlzIGZvciBnZW5lIHNldHMgd2l0aCBzaXplIGxlc3MgdGhhbgoyMDAiLCByZXNwZWN0aXZlbHkuCgpVc2luZyB0aGUgdGhyZXNob2xkcyBtZW50aW9uZWQgcHJldmlvdXNseSwgR086QlAgZm91bmQgMTMwIHBhdGh3YXlzLCBSRUFDIGZvdW5kIDM3LCBhbmQgV1AgZm91bmQgMTUuIFdoZW4gd2UgcHV0IGJvdGgKdGhlIHVwcmVndWxhdGVkIGdlbmVzIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzIHRvZ2V0aGVyIHRocm91Z2ggb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcywgaXQgYXBwZWFycyB0aGF0IHdlIGdldCBhCmJldHRlciBvdmVyYWxsIGltYWdlIG9mIHRoZSBwYXRod2F5cyB0aGF0IEFEIGFmZmVjdHMuIEZvciBleGFtcGxlLCB0aGUgdG9wIEdPOkJQIGhpdCBpcyBub3cgZGlyZWN0bHkgcmVsYXRlZCB0byBza2luCmRldmVsb3BtZW50LCBhbiBvYnZpb3VzIGxpbmsgdG8gQUQuIEluIGFkZGl0aW9uLCB3ZSBzZWUgb3RoZXIgcGF0aHdheXMgcmVsYXRlZCB0byBpbW11bmUgZnVuY3Rpb247IGZvciBleGFtcGxlLCB0aGUKVG9sbC1saWtlIHJlY2VwdG9yIDQgKFRMUjQpIGNhc2NhZGUsIHdoaWNoIGlzIHJlc3BvbnNpYmxlIGZvciB0aGUgcHJvZHVjdGlvbiBvZiBjeXRva2luZXMuCgpTaW5jZSBtYW55IG9mIHRoZXNlIHBhdGh3YXlzIHdlcmUgbm90IHByZXNlbnQgd2hlbiBvbmx5IHRoZSB1cC0gb3IgZG93bnJlZ3VsYXRlZCBnZW5lcyB3ZXJlIGNvbnNpZGVyZWQsIGl0IGlzIHBvc3NpYmxlCnRvIGNvbmNsdWRlIHRoYXQgQUQgaXMgaW52b2x2ZWQgaW4gbW9yZSBjb21wbGV4IHBhdGh3YXlzIHRoYXQgY2Fubm90IGJlIHJlc3RyaWN0ZWQgdG8ganVzdCBvbmUgdHlwZSBvZiBnZW5lLiBUaGUgZW50aXJlCnNldCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgbmVlZHMgdG8gYmUgY29uc2lkZXJlZCwgYW5kIG9ubHkgdGhlbiB3aWxsIHRoZSByZWxldmFudCBwYXRod2F5cyBhcHBlYXIuCgojIyA0LiBJbnRlcnByZXRhdGlvbgoKV2l0aCByZWdhcmQgdG8gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLCBzZXZlcmFsIG9mIHRoZSBtb3N0IHNpZ25pZmljYW50IGdlbmVzIHRoYXQgd2VyZSBmb3VuZCB3ZXJlIGFsc28KZm91bmQgYnkgdGhlIGF1dGhvcnMgb2YgdGhlIG9yaWdpbmFsIHBhcGVyLiBGb3IgZXhhbXBsZSwgb25lIHdpdGggYSBwYXJ0aWN1bGFybHkgaGlnaCBsZXZlbCBvZiBleHByZXNzaW9uIHdhcyBJTDE5LAp3aXRoIHRoZSBwYXBlciBzdGF0aW5nIHRoYXQgdGhleSAiZGV0ZWN0ZWQgYSBub3RhYmxlIGV4cHJlc3Npb24gb2YgSUwxOSAuLi4gaW4gQUwiLCBleGFjdGx5IHdoYXQgd2FzIGZvdW5kIGhlcmUuCkhvd2V2ZXIsIHRoaXMgYW5hbHlzaXMgZGlkIGZpbmQgc29tZSBoaWdobHkgZXhwcmVzc2VkIGdlbmVzIHRoYXQgd2VyZSBub3Qgb2Ygbm90ZSBpbiB0aGUgcGFwZXI7IGZvciBleGFtcGxlLCBOUFRYMiwKcmVsYXRlZCB0byB0aGUgQy1yZWFjdGl2ZSBwcm90ZWluLCB3YXMgbm90IG5vdGVkIGluIHRoZSBwYXBlci4gQWRkaXRpb25hbGx5LCBvdGhlciBnZW5lcyBsaWtlIElMMzEgYW5kIElMMjMgdGhhdCB3ZXJlCm5vdGVkIGluIHRoZSBwYXBlciB3ZXJlIG5vdCBhbW9uZyB0aGUgbW9zdCBleHByZXNzZWQgZ2VuZXMgaGVyZS4gVGh1cywgd2UgY2FuIHNheSB0aGF0IGZvciB0aGlzIHBhcnQgb2YgdGhlIG5vdGVib29rLAp0aGUgcmVzdWx0cyBhcmUgYXQgbGVhc3QgcGFydGlhbGx5IHN1cHBvcnRlZCBieSB0aGUgb3JpZ2luYWwgcGFwZXIuCgpXaGVuIGxvb2tpbmcgYXQgcGF0aHdheXMsIHRoZSByZXN1bHQgb2Ygb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcywgd2Ugc2VlIHNvbWUgc2ltaWxhcml0aWVzIGJldHdlZW4gdGhvc2UgdGhhdCB3ZXJlCmZvdW5kIGhlcmUgYW5kIHRob3NlIHRoYXQgd2VyZSBmb3VuZCBpbiB0aGUgb3JpZ2luYWwgcGFwZXIuIFRoZSBwYXBlciBzdGF0ZXMgdGhhdCB0aGUgImNvcmUgdHJhbnNjcmlwdG9tZSBzaWduYXR1cmUiIG9mCkFEIHBhdGllbnRzIGlzIGNoYXJhY3Rlcml6ZWQgYnkgZXBpZGVybWFsIGRpZmZlcmVudGlhdGlvbiBhbmQgaXRjaCBwYXRod2F5cywgYW5kIGdpdmVzIHNlYmFjZW91cyBnbGFuZHMgYXMgYW4gZXhhbXBsZS4KSW4gdGhlIE9SQSBvZiBhbGwgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLCB0aGUgaGlnaGVzdCBzY29yZWQgcGF0aHdheSBpcyByZWxhdGVkIHRvIHNraW4gZGV2ZWxvcG1lbnQsIG9mIHdoaWNoCiJzZWJhY2VvdXMgZ2xhbmQgZGV2ZWxvcG1lbnQiIGlzIGEgY2hpbGQuCgpBZGRpdGlvbmFsIHBhdGh3YXlzIHRoYXQgd2VyZSBmb3VuZCBpbiBib3RoIGluY2x1ZGUgZ2VuZXJhbCBUb2xsLWxpa2UgcmVjZXB0b3Igc2lnbmFsbGluZywgaW50ZXJsZXVraW4gc2lnbmFsbGluZywgYW5kCnNldmVyYWwgb3RoZXIgaW1tdW5lLXJlbGF0ZWQgcGF0aHdheXMuIEhvd2V2ZXIsIHRoZSBwYXBlciBkb2VzIG1lbnRpb24gc3BlY2lmaWMgZXhhbXBsZXMgbGlrZSBUaDE3IGNlbGwgZGlmZmVyZW50aWF0aW9uCmFuZCB0aGUgSUwtMTcgc2lnbmFsbGluZyBwYXRod2F5LCB3aGljaCB3ZXJlIG5vdCBleGFjdGx5IGZvdW5kIGluIHRoZSBPUkEuIFRodXMsIHdlIHNob3VsZCBiZSBhYmxlIHRvIGNvbmNsdWRlIHRoYXQgZm9yCnRoaXMgcGFydCBvZiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIG92ZXJhbGwgYXJlIHN1cHBvcnRlZCBieSB0aGUgb3JpZ2luYWwgcGFwZXIsIGJ1dCBpbiBvcmRlciB0byBjb25maXJtIHRoZQpwcmVzZW5jZSBvZiBtb3JlIHNwZWNpZmljIHBhdGh3YXlzLCBtb3JlIGRldGFpbGVkIGV4YW1pbmF0aW9uIG11c3QgYmUgZG9uZS4KCldoZW4gY29uc2lkZXJpbmcgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhbmQgcGF0aHdheXMsIGFub3RoZXIgd2F5IG9mIGxvb2tpbmcgZm9yIGV2aWRlbmNlIHRvIHN1cHBvcnQgdGhhdAp0aGUgYW5hbHlzZXMgaW4gdGhpcyBub3RlYm9vayAob3RoZXIgdGhhbiBjb21wYXJpbmcgd2l0aCB0aGUgb3JpZ2luYWwgcGFwZXIpIGlzIHRvIGxvb2sgZm9yIGFkZGl0aW9uYWwgcGFwZXJzIHRoYXQKaGF2ZSByZWFjaGVkIHRoZSBzYW1lIGNvbmNsdXNpb24uIE9uZSBzdHVkeSBbQG9rYV0gc2hvd2VkIHRoYXQgSUwtMTksIG9uZSBvZiB0aGUgbW9zdCBzaWduaWZpY2FudGx5IGV4cHJlc3NlZAppbnRlcmxldWtpbiBnZW5lcyBmb3VuZCBoZXJlLCB3YXMgbm90YWJseSBpbmNyZWFzZWQgaW4gcGF0aWVudHMgd2l0aCBBRC4gU3RhdGlzdGljYWwgYW5hbHlzaXMgKHByaW1hcmlseSBkb25lIHZpYSB0aGUKTWFubi1XaGl0bmV5IFUgdGVzdCkgd2FzIHBlcmZvcm1lZCwgYW5kIGluIHN1bW1hcnksIGNvbmNsdWRlZCB0aGF0IHRoZXJlIHdhcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGV2aWRlbmNlIHRvCnN1Z2dlc3QgdGhhdCBBRCBwYXRpZW50cyBoYWQgZWxldmF0ZWQgSUwtMTkgcHJvdGVpbiBsZXZlbHMsIGEgcmVmbGVjdGlvbiBvZiBzaWduaWZpY2FudCBleHByZXNzaW9uLgoKQSBzZWNvbmQgcGFwZXIgW0BwYW56ZXJdIGZvdW5kIHRoYXQgQUQgcGF0aWVudHMgYWxzbyBoYXZlIHNpZ25pZmljYW50IGxldmVscyBvZiBUTFI0IGV4cHJlc3Npb24gYXMgcGFydCBvZgpUb2xsLWxpa2UgcmVjZXB0b3Igc2lnbmFsbGluZyBwYXRod2F5cy4gU2FtcGxlcyBmcm9tIGxlc2lvbmFsIHNraW4gd2VyZSB0YWtlbiwgaW5jdWJhdGVkIHdpdGggVExSNCBhbnRpYm9kaWVzLCBhbmQgdGhlbgpSTkEgd2FzIGV4dHJhY3RlZCwgd2hpY2ggd2FzIGZvdW5kIHRvIHNob3cgY29uc3RpdHV0aXZlIGV4cHJlc3Npb24gb2YgVExSNC4gVGhlcmUgaGF2ZSBldmVuIGJlZW4gcHJldmlvdXMgc3R1ZGllcyBvbgp1c2luZyBkdXBpbHVtYWIgYW5kIGN5Y2xvc3BvcmluZSB0byB0cmVhdCBBRDsgYSB0aGlkIHBhcGVyIFtAbWFuc291cmldIHNwZWNpZmljYWxseSBzdHVkaWVkIHRoZSBlZmZlY3RzIG9mIHRoZXNlIGRydWdzCm9uIGh1bWFuIGltbXVuZSBwYXRod2F5cywgYW5kIGZvdW5kIHRoYXQgdGhleSBhcmUgYWJsZSB0byBibG9jayBzb21lIGludGVybGV1a2luIHNpZ25hbGxpbmcgcGF0aHdheXMsIHdoaWNoIGluIHR1cm4KcmVzdWx0ZWQgaW4gY2xpbmljYWwgaW1wcm92ZW1lbnRzLiBUaHVzLCB0aGlzIHNob3dzIHRoYXQgdGhlcmUgaXMgZnVydGhlciBzdXBwb3J0IGZvciB0aGUgcGF0aHdheXMgZGlzY292ZXJlZCBieSB0aGUKT1JBIGNvbmR1Y3RlZCBoZXJlLgoKIyMgNS4gUmVmZXJlbmNlcwo=